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