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