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