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