ElementsTable.java revision 3824:376ee1fd40c3
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            if (mdle != null)
624                imodules.add(mdle);
625            ipackages.add(pkg);
626        });
627
628        // process types
629        Set<TypeElement> iclasses = new LinkedHashSet<>();
630        // add all types enclosed in expanded modules and packages
631        ipackages.forEach((pkg) -> {
632            addAllClasses(iclasses, pkg);
633        });
634        // add all types and its nested types
635        specifiedTypeElements.forEach((klass) -> {
636            ModuleElement mdle = toolEnv.elements.getModuleOf(klass);
637            if (mdle != null && !mdle.isUnnamed())
638                imodules.add(mdle);
639            PackageElement pkg = toolEnv.elements.getPackageOf(klass);
640            ipackages.add(pkg);
641            addAllClasses(iclasses, klass, true);
642        });
643
644        // all done, freeze the collections
645        includedModuleElements = Collections.unmodifiableSet(imodules);
646        includedPackageElements = Collections.unmodifiableSet(ipackages);
647        includedTypeElements = Collections.unmodifiableSet(iclasses);
648    }
649
650    /*
651     * Computes the included packages and freezes the specified packages list.
652     */
653    private void computeSpecifiedPackages() throws ToolException {
654
655        computeSubpackages();
656
657        Set<PackageElement> packlist = new LinkedHashSet<>();
658        cmdLinePackages.forEach((modpkg) -> {
659            ModuleElement mdle = null;
660            PackageElement pkg;
661            if (modpkg.hasModule()) {
662                mdle = toolEnv.elements.getModuleElement(modpkg.moduleName);
663                pkg = toolEnv.elements.getPackageElement(mdle, modpkg.packageName);
664            } else {
665                pkg = toolEnv.elements.getPackageElement(modpkg.toString());
666            }
667
668            if (pkg != null) {
669                packlist.add(pkg);
670            } else {
671                messager.printWarningUsingKey("main.package_not_found", modpkg.toString());
672            }
673        });
674        specifiedPackageElements = Collections.unmodifiableSet(packlist);
675    }
676
677    /**
678     * Adds all classes as well as inner classes, to the specified
679     * list.
680     */
681    private void computeSpecifiedTypes() throws ToolException {
682        Set<TypeElement> classes = new LinkedHashSet<>();
683          classDecList.forEach((def) -> {
684            TypeElement te = (TypeElement) def.sym;
685            if (te != null) {
686                addAllClasses(classes, te, true);
687            }
688        });
689        for (String className : classArgList) {
690            TypeElement te = toolEnv.loadClass(className);
691            if (te == null) {
692                String text = messager.getText("javadoc.class_not_found", className);
693                throw new ToolException(CMDERR, text);
694            } else {
695                addAllClasses(classes, te, true);
696            }
697        }
698        specifiedTypeElements = Collections.unmodifiableSet(classes);
699    }
700
701    private void addFilesForParser(Collection<JavaFileObject> result,
702            Collection<ModulePackage> collection,
703            boolean recurse) throws ToolException {
704        for (ModulePackage modpkg : collection) {
705            toolEnv.notice("main.Loading_source_files_for_package", modpkg.toString());
706            List<JavaFileObject> files = getFiles(modpkg, recurse);
707            if (files.isEmpty()) {
708                String text = messager.getText("main.no_source_files_for_package",
709                        modpkg.toString());
710                throw new ToolException(CMDERR, text);
711            } else {
712                result.addAll(files);
713            }
714        }
715    }
716
717    /**
718     * Returns an aggregated list of java file objects from the items
719     * specified on the command line. The packages specified should not
720     * recurse, however sub-packages should recurse into the sub directories.
721     * @return a list of java file objects
722     * @throws IOException if an error occurs
723     */
724    List<JavaFileObject> getFilesToParse() throws ToolException {
725        List<JavaFileObject> result = new ArrayList<>();
726        addFilesForParser(result, cmdLinePackages, false);
727        addFilesForParser(result, subPackages, true);
728        return result;
729    }
730
731    /**
732     * Returns the set of source files for a package.
733     *
734     * @param packageName the specified package
735     * @return the set of file objects for the specified package
736     * @throws ToolException if an error occurs while accessing the files
737     */
738    private List<JavaFileObject> getFiles(ModulePackage modpkg,
739            boolean recurse) throws ToolException {
740        Entry e = getEntry(modpkg);
741        // The files may have been found as a side effect of searching for subpackages
742        if (e.files != null) {
743            return e.files;
744        }
745
746        ListBuffer<JavaFileObject> lb = new ListBuffer<>();
747        Location packageLocn = getLocation(modpkg);
748        if (packageLocn == null) {
749            return Collections.emptyList();
750        }
751        String pname = modpkg.packageName;
752
753        try {
754            for (JavaFileObject fo : fm.list(packageLocn, pname, sourceKinds, recurse)) {
755                String binaryName = fm.inferBinaryName(packageLocn, fo);
756                String simpleName = getSimpleName(binaryName);
757                if (isValidClassName(simpleName)) {
758                    lb.append(fo);
759                }
760            }
761        } catch (IOException ioe) {
762            String text = messager.getText("main.file.manager.list", pname);
763            throw new ToolException(SYSERR, text, ioe);
764        }
765
766        return lb.toList();
767    }
768
769    private ModuleSymbol findModuleOfPackageName(String packageName) {
770            Name pack = names.fromString(packageName);
771            for (ModuleSymbol msym : modules.allModules()) {
772                PackageSymbol p = syms.getPackage(msym, pack);
773                if (p != null && !p.members().isEmpty()) {
774                    return msym;
775                }
776            }
777            return null;
778    }
779
780    private Location getLocation(ModulePackage modpkg) throws ToolException {
781        if (location != StandardLocation.MODULE_SOURCE_PATH) {
782            return location;
783        }
784
785        if (modpkg.hasModule()) {
786            return getModuleLocation(location, modpkg.moduleName);
787        }
788        // TODO: handle invalid results better.
789        ModuleSymbol msym = findModuleOfPackageName(modpkg.packageName);
790        if (msym == null) {
791            return null;
792        }
793        return getModuleLocation(location, msym.name.toString());
794    }
795
796    private Location getModuleLocation(Location location, String msymName)
797            throws ToolException {
798        try {
799            return fm.getLocationForModule(location, msymName);
800        } catch (IOException ioe) {
801            String text = messager.getText("main.doclet_could_not_get_location", msymName);
802            throw new ToolException(ERROR, text, ioe);
803        }
804    }
805
806    private Entry getEntry(String name) {
807        return getEntry(new ModulePackage(name));
808    }
809
810    private Entry getEntry(ModulePackage modpkg) {
811        Entry e = entries.get(modpkg.packageName);
812        if (e == null) {
813            entries.put(modpkg.packageName, e = new Entry(modpkg));
814        }
815        return e;
816    }
817
818    private String getPackageName(String name) {
819        int lastDot = name.lastIndexOf(".");
820        return (lastDot == -1 ? "" : name.substring(0, lastDot));
821    }
822
823    private String getSimpleName(String name) {
824        int lastDot = name.lastIndexOf(".");
825        return (lastDot == -1 ? name : name.substring(lastDot + 1));
826    }
827
828    /**
829     * Adds all inner classes of this class, and their inner classes recursively, to the list
830     */
831    private void addAllClasses(Collection<TypeElement> list, TypeElement typeElement, boolean filtered) {
832        ClassSymbol klass = (ClassSymbol)typeElement;
833        try {
834            // eliminate needless checking, do this first.
835            if (list.contains(klass)) return;
836            // ignore classes with invalid Java class names
837            if (!JavadocTool.isValidClassName(klass.name.toString())) return;
838            if (filtered && !isTypeElementSelected(klass)) return;
839            list.add(klass);
840            for (Symbol sym : klass.members().getSymbols(NON_RECURSIVE)) {
841                if (sym != null && sym.kind == Kind.TYP) {
842                    ClassSymbol s = (ClassSymbol)sym;
843                    addAllClasses(list, s, filtered);
844                }
845            }
846        } catch (CompletionFailure e) {
847            if (e.getMessage() != null)
848                messager.printWarning(e.getMessage());
849            else
850                messager.printWarningUsingKey("main.unexpected.exception", e);
851        }
852    }
853
854    /**
855     * Returns a list of all classes contained in this package, including
856     * member classes of those classes, and their member classes, etc.
857     */
858    private void addAllClasses(Collection<TypeElement> list, PackageElement pkg) {
859        boolean filtered = true;
860        PackageSymbol sym = (PackageSymbol)pkg;
861        for (Symbol isym : sym.members().getSymbols(NON_RECURSIVE)) {
862            addAllClasses(list, (TypeElement)isym, filtered);
863        }
864    }
865
866    private boolean isTypeElementSelected(TypeElement te) {
867        return (xclasses || toolEnv.isFromSource(te)) && isSelected(te);
868    }
869
870    SimpleElementVisitor9<Boolean, Void> visibleElementVisitor = null;
871    /**
872     * Returns true if the element is selected, by applying
873     * the access filter checks. Special treatment is applied to
874     * types, for a top level type the access filter applies completely,
875     * however if is a nested type then it is allowed either  if
876     * the enclosing is a static or the enclosing is also selected.
877     *
878     * @param e the element to be checked
879     * @return true if the element is visible
880     */
881    public boolean isSelected(Element e) {
882        if (toolEnv.isSynthetic((Symbol) e)) {
883            return false;
884        }
885        if (visibleElementVisitor == null) {
886            visibleElementVisitor = new SimpleElementVisitor9<Boolean, Void>() {
887                @Override
888                public Boolean visitType(TypeElement e, Void p) {
889                    if (!accessFilter.checkModifier(e)) {
890                        return false; // it is not allowed
891                    }
892                    Element encl = e.getEnclosingElement();
893
894                    // check if nested
895                    if (encl.getKind() == ElementKind.PACKAGE)
896                        return true; // top-level class, allow it
897
898                    // is enclosed static
899                    if (encl.getModifiers().contains(Modifier.STATIC))
900                        return true; // allowed
901
902                    // check the enclosing
903                    return visit(encl);
904                }
905
906                @Override
907                protected Boolean defaultAction(Element e, Void p) {
908                    return accessFilter.checkModifier(e);
909                }
910
911                @Override
912                public Boolean visitUnknown(Element e, Void p) {
913                    throw new AssertionError("unkown element: " + p);
914                }
915            };
916        }
917        return visibleElementVisitor.visit(e);
918    }
919
920    private class IncludedVisitor extends SimpleElementVisitor9<Boolean, Void> {
921        final private Set<Element> includedCache;
922
923        public IncludedVisitor() {
924            includedCache = new LinkedHashSet<>();
925        }
926
927        @Override
928        public Boolean visitModule(ModuleElement e, Void p) {
929            // deduced by specified and/or requires expansion
930            return includedModuleElements.contains(e);
931        }
932
933        @Override
934        public Boolean visitPackage(PackageElement e, Void p) {
935            // deduced by specified or downward expansions
936            return includedPackageElements.contains(e);
937        }
938
939        @Override
940        public Boolean visitType(TypeElement e, Void p) {
941            if (includedTypeElements.contains(e)) {
942                return true;
943            }
944            if (isTypeElementSelected(e)) {
945                // Class is nameable from top-level and
946                // the class and all enclosing classes
947                // pass the modifier filter.
948                PackageElement pkg = toolEnv.elements.getPackageOf(e);
949                if (specifiedPackageElements.contains(pkg)) {
950                    return true;
951                }
952                Element enclosing = e.getEnclosingElement();
953                if (enclosing != null) {
954                    switch(enclosing.getKind()) {
955                        case PACKAGE:
956                            return specifiedPackageElements.contains((PackageElement)enclosing);
957                        case CLASS: case INTERFACE: case ENUM: case ANNOTATION_TYPE:
958                            return visit((TypeElement) enclosing);
959                        default:
960                            throw new AssertionError("unknown element: " + enclosing);
961                    }
962                }
963            }
964            return false;
965        }
966
967        // members
968        @Override
969        public Boolean defaultAction(Element e, Void p) {
970            if (includedCache.contains(e))
971                return true;
972            if (visit(e.getEnclosingElement()) && isSelected(e)) {
973                switch(e.getKind()) {
974                    case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE:
975                    case MODULE: case OTHER: case PACKAGE:
976                        throw new AssertionError("invalid element for this operation: " + e);
977                    default:
978                        // the only allowed kinds in the cache are "members"
979                        includedCache.add(e);
980                        return true;
981                }
982            }
983            return false;
984        }
985
986        @Override
987        public Boolean visitUnknown(Element e, Void p) {
988            throw new AssertionError("unknown element: " + e);
989        }
990
991    }
992
993    class Entry {
994        final ModulePackage modpkg;
995        Boolean excluded = false;
996        com.sun.tools.javac.util.List<JavaFileObject> files;
997
998        Entry(ModulePackage modpkg) {
999            this.modpkg = modpkg;
1000        }
1001
1002        Entry(String name) {
1003            modpkg = new ModulePackage(name);
1004        }
1005
1006        boolean isExcluded() {
1007            return excluded;
1008        }
1009
1010        @Override
1011        public String toString() {
1012            return "Entry{" + "modpkg=" + modpkg + ", excluded=" + excluded + ", files=" + files + '}';
1013        }
1014    }
1015
1016    /**
1017     * A container class to retrieve the module and package pair
1018     * from a parsed qualified package name.
1019     */
1020    static class ModulePackage {
1021
1022        public final String moduleName;
1023        public final String packageName;
1024
1025        ModulePackage(String modulename, String packagename) {
1026            this.moduleName = modulename;
1027            this.packageName = packagename;
1028        }
1029
1030        ModulePackage(ModuleElement msym, String packagename) {
1031            this.moduleName = msym.toString();
1032            this.packageName = packagename;
1033        }
1034
1035        ModulePackage(String name) {
1036            String a[] = name.split("/");
1037            if (a.length == 2) {
1038                this.moduleName = a[0];
1039                this.packageName = a[1];
1040            } else {
1041                moduleName = null;
1042                packageName = name;
1043            }
1044        }
1045
1046        boolean hasModule() {
1047            return this.moduleName != null;
1048        }
1049
1050        @Override
1051        public boolean equals(Object obj) {
1052            if (obj instanceof ModulePackage) {
1053                ModulePackage that = (ModulePackage)obj;
1054                return this.toString().equals(that.toString());
1055            }
1056            return false;
1057        }
1058
1059        @Override
1060        public int hashCode() {
1061             return toString().hashCode();
1062        }
1063
1064        @Override
1065        public String toString() {
1066            return moduleName == null ? packageName : moduleName + "/" + packageName;
1067        }
1068    }
1069
1070    /**
1071     * A class which filters the access flags on classes, fields, methods, etc.
1072     *
1073     * @see javax.lang.model.element.Modifier
1074     */
1075
1076    static class ModifierFilter {
1077        /**
1078         * The allowed ElementKind that can be stored.
1079         */
1080        static final EnumSet<ElementKind> ALLOWED_KINDS = EnumSet.of(ElementKind.METHOD,
1081                                                    ElementKind.CLASS,
1082                                                    ElementKind.PACKAGE,
1083                                                    ElementKind.MODULE);
1084
1085        // all possible accesss levels allowed for each element
1086        private final EnumMap<ElementKind, EnumSet<AccessKind>> filterMap =
1087                new EnumMap<>(ElementKind.class);
1088
1089        // the specified access level for each element
1090        private final EnumMap<ElementKind, AccessKind> accessMap =
1091                new EnumMap<>(ElementKind.class);
1092
1093        /**
1094         * Constructor - Specify a filter.
1095         *
1096         * @param accessSet an Access filter.
1097         */
1098        ModifierFilter(Map<ToolOption, Object> opts) {
1099
1100            AccessKind accessValue = null;
1101            for (ElementKind kind : ALLOWED_KINDS) {
1102                switch (kind) {
1103                    case METHOD:
1104                        accessValue  = (AccessKind)opts.get(ToolOption.SHOW_MEMBERS);
1105                        break;
1106                    case CLASS:
1107                        accessValue  = (AccessKind)opts.get(ToolOption.SHOW_TYPES);
1108                        break;
1109                    case PACKAGE:
1110                        accessValue  = (AccessKind)opts.get(ToolOption.SHOW_PACKAGES);
1111                        break;
1112                    case MODULE:
1113                        accessValue  = (AccessKind)opts.get(ToolOption.SHOW_MODULE_CONTENTS);
1114                        break;
1115                    default:
1116                        throw new AssertionError("unknown element: " + kind);
1117
1118                }
1119                accessMap.put(kind, accessValue);
1120                filterMap.put(kind, getFilterSet(accessValue));
1121            }
1122        }
1123
1124        static EnumSet<AccessKind> getFilterSet(AccessKind acccessValue) {
1125            switch (acccessValue) {
1126                case PUBLIC:
1127                    return EnumSet.of(AccessKind.PUBLIC);
1128                case PROTECTED:
1129                default:
1130                    return EnumSet.of(AccessKind.PUBLIC, AccessKind.PROTECTED);
1131                case PACKAGE:
1132                    return EnumSet.of(AccessKind.PUBLIC, AccessKind.PROTECTED, AccessKind.PACKAGE);
1133                case PRIVATE:
1134                    return EnumSet.allOf(AccessKind.class);
1135            }
1136        }
1137
1138        public AccessKind getAccessValue(ElementKind kind) {
1139            if (!ALLOWED_KINDS.contains(kind)) {
1140                throw new IllegalArgumentException("not allowed: " + kind);
1141            }
1142            return accessMap.getOrDefault(kind, AccessKind.PROTECTED);
1143        }
1144
1145        /**
1146         * Returns true if access is allowed.
1147         *
1148         * @param e the element in question
1149         * @return whether the modifiers pass this filter
1150         */
1151        public boolean checkModifier(Element e) {
1152            Set<Modifier> modifiers = e.getModifiers();
1153            AccessKind fflag = AccessKind.PACKAGE;
1154            if (modifiers.contains(Modifier.PUBLIC)) {
1155                fflag = AccessKind.PUBLIC;
1156            } else if (modifiers.contains(Modifier.PROTECTED)) {
1157                fflag = AccessKind.PROTECTED;
1158            } else if (modifiers.contains(Modifier.PRIVATE)) {
1159                fflag = AccessKind.PRIVATE;
1160            }
1161            EnumSet<AccessKind> filterSet = filterMap.get(getAllowedKind(e.getKind()));
1162            return filterSet.contains(fflag);
1163        }
1164
1165        // convert a requested element kind to an allowed access kind
1166        private ElementKind getAllowedKind(ElementKind kind) {
1167            switch (kind) {
1168                case CLASS: case METHOD: case MODULE: case PACKAGE:
1169                    return kind;
1170                case ANNOTATION_TYPE: case ENUM: case INTERFACE:
1171                    return ElementKind.CLASS;
1172                case CONSTRUCTOR: case ENUM_CONSTANT: case EXCEPTION_PARAMETER:
1173                case FIELD: case INSTANCE_INIT: case LOCAL_VARIABLE: case PARAMETER:
1174                case RESOURCE_VARIABLE: case STATIC_INIT: case TYPE_PARAMETER:
1175                    return ElementKind.METHOD;
1176                default:
1177                    throw new AssertionError("unsupported kind: " + kind);
1178            }
1179        }
1180    } // end ModifierFilter
1181}
1182