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