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