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