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