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