ElementsTable.java revision 3793:5a2b9f22ba5d
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 imodules.add(mdle); 624 ipackages.add(pkg); 625 }); 626 627 // process types 628 Set<TypeElement> iclasses = new LinkedHashSet<>(); 629 // add all types enclosed in expanded modules and packages 630 ipackages.forEach((pkg) -> { 631 addAllClasses(iclasses, pkg); 632 }); 633 // add all types and its nested types 634 specifiedTypeElements.forEach((klass) -> { 635 ModuleElement mdle = toolEnv.elements.getModuleOf(klass); 636 if (!mdle.isUnnamed()) 637 imodules.add(mdle); 638 PackageElement pkg = toolEnv.elements.getPackageOf(klass); 639 ipackages.add(pkg); 640 addAllClasses(iclasses, klass, true); 641 }); 642 643 // all done, freeze the collections 644 includedModuleElements = Collections.unmodifiableSet(imodules); 645 includedPackageElements = Collections.unmodifiableSet(ipackages); 646 includedTypeElements = Collections.unmodifiableSet(iclasses); 647 } 648 649 /* 650 * Computes the included packages and freezes the specified packages list. 651 */ 652 private void computeSpecifiedPackages() throws ToolException { 653 654 computeSubpackages(); 655 656 Set<PackageElement> packlist = new LinkedHashSet<>(); 657 cmdLinePackages.forEach((modpkg) -> { 658 ModuleElement mdle = null; 659 PackageElement pkg; 660 if (modpkg.hasModule()) { 661 mdle = toolEnv.elements.getModuleElement(modpkg.moduleName); 662 pkg = toolEnv.elements.getPackageElement(mdle, modpkg.packageName); 663 } else { 664 pkg = toolEnv.elements.getPackageElement(modpkg.toString()); 665 } 666 667 if (pkg != null) { 668 packlist.add(pkg); 669 } else { 670 messager.printWarningUsingKey("main.package_not_found", modpkg.toString()); 671 } 672 }); 673 specifiedPackageElements = Collections.unmodifiableSet(packlist); 674 } 675 676 /** 677 * Adds all classes as well as inner classes, to the specified 678 * list. 679 */ 680 private void computeSpecifiedTypes() throws ToolException { 681 Set<TypeElement> classes = new LinkedHashSet<>(); 682 classDecList.forEach((def) -> { 683 TypeElement te = (TypeElement) def.sym; 684 if (te != null) { 685 addAllClasses(classes, te, true); 686 } 687 }); 688 for (String className : classArgList) { 689 TypeElement te = toolEnv.loadClass(className); 690 if (te == null) { 691 String text = messager.getText("javadoc.class_not_found", className); 692 throw new ToolException(CMDERR, text); 693 } else { 694 addAllClasses(classes, te, true); 695 } 696 } 697 specifiedTypeElements = Collections.unmodifiableSet(classes); 698 } 699 700 private void addFilesForParser(Collection<JavaFileObject> result, 701 Collection<ModulePackage> collection, 702 boolean recurse) throws ToolException { 703 for (ModulePackage modpkg : collection) { 704 toolEnv.notice("main.Loading_source_files_for_package", modpkg.toString()); 705 List<JavaFileObject> files = getFiles(modpkg, recurse); 706 if (files.isEmpty()) { 707 String text = messager.getText("main.no_source_files_for_package", 708 modpkg.toString()); 709 throw new ToolException(CMDERR, text); 710 } else { 711 result.addAll(files); 712 } 713 } 714 } 715 716 /** 717 * Returns an aggregated list of java file objects from the items 718 * specified on the command line. The packages specified should not 719 * recurse, however sub-packages should recurse into the sub directories. 720 * @return a list of java file objects 721 * @throws IOException if an error occurs 722 */ 723 List<JavaFileObject> getFilesToParse() throws ToolException { 724 List<JavaFileObject> result = new ArrayList<>(); 725 addFilesForParser(result, cmdLinePackages, false); 726 addFilesForParser(result, subPackages, true); 727 return result; 728 } 729 730 /** 731 * Returns the set of source files for a package. 732 * 733 * @param packageName the specified package 734 * @return the set of file objects for the specified package 735 * @throws ToolException if an error occurs while accessing the files 736 */ 737 private List<JavaFileObject> getFiles(ModulePackage modpkg, 738 boolean recurse) throws ToolException { 739 Entry e = getEntry(modpkg); 740 // The files may have been found as a side effect of searching for subpackages 741 if (e.files != null) { 742 return e.files; 743 } 744 745 ListBuffer<JavaFileObject> lb = new ListBuffer<>(); 746 Location packageLocn = getLocation(modpkg); 747 if (packageLocn == null) { 748 return Collections.emptyList(); 749 } 750 String pname = modpkg.packageName; 751 752 try { 753 for (JavaFileObject fo : fm.list(packageLocn, pname, sourceKinds, recurse)) { 754 String binaryName = fm.inferBinaryName(packageLocn, fo); 755 String simpleName = getSimpleName(binaryName); 756 if (isValidClassName(simpleName)) { 757 lb.append(fo); 758 } 759 } 760 } catch (IOException ioe) { 761 String text = messager.getText("main.file.manager.list", pname); 762 throw new ToolException(SYSERR, text, ioe); 763 } 764 765 return lb.toList(); 766 } 767 768 private ModuleSymbol findModuleOfPackageName(String packageName) { 769 Name pack = names.fromString(packageName); 770 for (ModuleSymbol msym : modules.allModules()) { 771 PackageSymbol p = syms.getPackage(msym, pack); 772 if (p != null && !p.members().isEmpty()) { 773 return msym; 774 } 775 } 776 return null; 777 } 778 779 private Location getLocation(ModulePackage modpkg) throws ToolException { 780 if (location != StandardLocation.MODULE_SOURCE_PATH) { 781 return location; 782 } 783 784 if (modpkg.hasModule()) { 785 return getModuleLocation(location, modpkg.moduleName); 786 } 787 // TODO: handle invalid results better. 788 ModuleSymbol msym = findModuleOfPackageName(modpkg.packageName); 789 if (msym == null) { 790 return null; 791 } 792 return getModuleLocation(location, msym.name.toString()); 793 } 794 795 private Location getModuleLocation(Location location, String msymName) 796 throws ToolException { 797 try { 798 return fm.getLocationForModule(location, msymName); 799 } catch (IOException ioe) { 800 String text = messager.getText("main.doclet_could_not_get_location", msymName); 801 throw new ToolException(ERROR, text, ioe); 802 } 803 } 804 805 private Entry getEntry(String name) { 806 return getEntry(new ModulePackage(name)); 807 } 808 809 private Entry getEntry(ModulePackage modpkg) { 810 Entry e = entries.get(modpkg.packageName); 811 if (e == null) { 812 entries.put(modpkg.packageName, e = new Entry(modpkg)); 813 } 814 return e; 815 } 816 817 private String getPackageName(String name) { 818 int lastDot = name.lastIndexOf("."); 819 return (lastDot == -1 ? "" : name.substring(0, lastDot)); 820 } 821 822 private String getSimpleName(String name) { 823 int lastDot = name.lastIndexOf("."); 824 return (lastDot == -1 ? name : name.substring(lastDot + 1)); 825 } 826 827 /** 828 * Adds all inner classes of this class, and their inner classes recursively, to the list 829 */ 830 private void addAllClasses(Collection<TypeElement> list, TypeElement typeElement, boolean filtered) { 831 ClassSymbol klass = (ClassSymbol)typeElement; 832 try { 833 // eliminate needless checking, do this first. 834 if (list.contains(klass)) return; 835 // ignore classes with invalid Java class names 836 if (!JavadocTool.isValidClassName(klass.name.toString())) return; 837 if (filtered && !isTypeElementSelected(klass)) return; 838 list.add(klass); 839 for (Symbol sym : klass.members().getSymbols(NON_RECURSIVE)) { 840 if (sym != null && sym.kind == Kind.TYP) { 841 ClassSymbol s = (ClassSymbol)sym; 842 addAllClasses(list, s, filtered); 843 } 844 } 845 } catch (CompletionFailure e) { 846 if (e.getMessage() != null) 847 messager.printWarning(e.getMessage()); 848 else 849 messager.printWarningUsingKey("main.unexpected.exception", e); 850 } 851 } 852 853 /** 854 * Returns a list of all classes contained in this package, including 855 * member classes of those classes, and their member classes, etc. 856 */ 857 private void addAllClasses(Collection<TypeElement> list, PackageElement pkg) { 858 boolean filtered = true; 859 PackageSymbol sym = (PackageSymbol)pkg; 860 for (Symbol isym : sym.members().getSymbols(NON_RECURSIVE)) { 861 addAllClasses(list, (TypeElement)isym, filtered); 862 } 863 } 864 865 private boolean isTypeElementSelected(TypeElement te) { 866 return (xclasses || toolEnv.isFromSource(te)) && isSelected(te); 867 } 868 869 SimpleElementVisitor9<Boolean, Void> visibleElementVisitor = null; 870 /** 871 * Returns true if the element is selected, by applying 872 * the access filter checks. Special treatment is applied to 873 * types, for a top level type the access filter applies completely, 874 * however if is a nested type then it is allowed either if 875 * the enclosing is a static or the enclosing is also selected. 876 * 877 * @param e the element to be checked 878 * @return true if the element is visible 879 */ 880 public boolean isSelected(Element e) { 881 if (toolEnv.isSynthetic((Symbol) e)) { 882 return false; 883 } 884 if (visibleElementVisitor == null) { 885 visibleElementVisitor = new SimpleElementVisitor9<Boolean, Void>() { 886 @Override 887 public Boolean visitType(TypeElement e, Void p) { 888 if (!accessFilter.checkModifier(e)) { 889 return false; // it is not allowed 890 } 891 Element encl = e.getEnclosingElement(); 892 893 // check if nested 894 if (encl.getKind() == ElementKind.PACKAGE) 895 return true; // top-level class, allow it 896 897 // is enclosed static 898 if (encl.getModifiers().contains(Modifier.STATIC)) 899 return true; // allowed 900 901 // check the enclosing 902 return visit(encl); 903 } 904 905 @Override 906 protected Boolean defaultAction(Element e, Void p) { 907 return accessFilter.checkModifier(e); 908 } 909 910 @Override 911 public Boolean visitUnknown(Element e, Void p) { 912 throw new AssertionError("unkown element: " + p); 913 } 914 }; 915 } 916 return visibleElementVisitor.visit(e); 917 } 918 919 private class IncludedVisitor extends SimpleElementVisitor9<Boolean, Void> { 920 final private Set<Element> includedCache; 921 922 public IncludedVisitor() { 923 includedCache = new LinkedHashSet<>(); 924 } 925 926 @Override 927 public Boolean visitModule(ModuleElement e, Void p) { 928 // deduced by specified and/or requires expansion 929 return includedModuleElements.contains(e); 930 } 931 932 @Override 933 public Boolean visitPackage(PackageElement e, Void p) { 934 // deduced by specified or downward expansions 935 return includedPackageElements.contains(e); 936 } 937 938 @Override 939 public Boolean visitType(TypeElement e, Void p) { 940 if (includedTypeElements.contains(e)) { 941 return true; 942 } 943 if (isTypeElementSelected(e)) { 944 // Class is nameable from top-level and 945 // the class and all enclosing classes 946 // pass the modifier filter. 947 PackageElement pkg = toolEnv.elements.getPackageOf(e); 948 if (specifiedPackageElements.contains(pkg)) { 949 return true; 950 } 951 Element enclosing = e.getEnclosingElement(); 952 if (enclosing != null) { 953 switch(enclosing.getKind()) { 954 case PACKAGE: 955 return specifiedPackageElements.contains((PackageElement)enclosing); 956 case CLASS: case INTERFACE: case ENUM: case ANNOTATION_TYPE: 957 return visit((TypeElement) enclosing); 958 default: 959 throw new AssertionError("unknown element: " + enclosing); 960 } 961 } 962 } 963 return false; 964 } 965 966 // members 967 @Override 968 public Boolean defaultAction(Element e, Void p) { 969 if (includedCache.contains(e)) 970 return true; 971 if (visit(e.getEnclosingElement()) && isSelected(e)) { 972 switch(e.getKind()) { 973 case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE: 974 case MODULE: case OTHER: case PACKAGE: 975 throw new AssertionError("invalid element for this operation: " + e); 976 default: 977 // the only allowed kinds in the cache are "members" 978 includedCache.add(e); 979 return true; 980 } 981 } 982 return false; 983 } 984 985 @Override 986 public Boolean visitUnknown(Element e, Void p) { 987 throw new AssertionError("unknown element: " + e); 988 } 989 990 } 991 992 class Entry { 993 final ModulePackage modpkg; 994 Boolean excluded = false; 995 com.sun.tools.javac.util.List<JavaFileObject> files; 996 997 Entry(ModulePackage modpkg) { 998 this.modpkg = modpkg; 999 } 1000 1001 Entry(String name) { 1002 modpkg = new ModulePackage(name); 1003 } 1004 1005 boolean isExcluded() { 1006 return excluded; 1007 } 1008 1009 @Override 1010 public String toString() { 1011 return "Entry{" + "modpkg=" + modpkg + ", excluded=" + excluded + ", files=" + files + '}'; 1012 } 1013 } 1014 1015 /** 1016 * A container class to retrieve the module and package pair 1017 * from a parsed qualified package name. 1018 */ 1019 static class ModulePackage { 1020 1021 public final String moduleName; 1022 public final String packageName; 1023 1024 ModulePackage(String modulename, String packagename) { 1025 this.moduleName = modulename; 1026 this.packageName = packagename; 1027 } 1028 1029 ModulePackage(ModuleElement msym, String packagename) { 1030 this.moduleName = msym.toString(); 1031 this.packageName = packagename; 1032 } 1033 1034 ModulePackage(String name) { 1035 String a[] = name.split("/"); 1036 if (a.length == 2) { 1037 this.moduleName = a[0]; 1038 this.packageName = a[1]; 1039 } else { 1040 moduleName = null; 1041 packageName = name; 1042 } 1043 } 1044 1045 boolean hasModule() { 1046 return this.moduleName != null; 1047 } 1048 1049 @Override 1050 public boolean equals(Object obj) { 1051 if (obj instanceof ModulePackage) { 1052 ModulePackage that = (ModulePackage)obj; 1053 return this.toString().equals(that.toString()); 1054 } 1055 return false; 1056 } 1057 1058 @Override 1059 public int hashCode() { 1060 return toString().hashCode(); 1061 } 1062 1063 @Override 1064 public String toString() { 1065 return moduleName == null ? packageName : moduleName + "/" + packageName; 1066 } 1067 } 1068 1069 /** 1070 * A class which filters the access flags on classes, fields, methods, etc. 1071 * 1072 * @see javax.lang.model.element.Modifier 1073 */ 1074 1075 static class ModifierFilter { 1076 /** 1077 * The allowed ElementKind that can be stored. 1078 */ 1079 static final EnumSet<ElementKind> ALLOWED_KINDS = EnumSet.of(ElementKind.METHOD, 1080 ElementKind.CLASS, 1081 ElementKind.PACKAGE, 1082 ElementKind.MODULE); 1083 1084 // all possible accesss levels allowed for each element 1085 private final EnumMap<ElementKind, EnumSet<AccessKind>> filterMap = 1086 new EnumMap<>(ElementKind.class); 1087 1088 // the specified access level for each element 1089 private final EnumMap<ElementKind, AccessKind> accessMap = 1090 new EnumMap<>(ElementKind.class); 1091 1092 /** 1093 * Constructor - Specify a filter. 1094 * 1095 * @param accessSet an Access filter. 1096 */ 1097 ModifierFilter(Map<ToolOption, Object> opts) { 1098 1099 AccessKind accessValue = null; 1100 for (ElementKind kind : ALLOWED_KINDS) { 1101 switch (kind) { 1102 case METHOD: 1103 accessValue = (AccessKind)opts.get(ToolOption.SHOW_MEMBERS); 1104 break; 1105 case CLASS: 1106 accessValue = (AccessKind)opts.get(ToolOption.SHOW_TYPES); 1107 break; 1108 case PACKAGE: 1109 accessValue = (AccessKind)opts.get(ToolOption.SHOW_PACKAGES); 1110 break; 1111 case MODULE: 1112 accessValue = (AccessKind)opts.get(ToolOption.SHOW_MODULE_CONTENTS); 1113 break; 1114 default: 1115 throw new AssertionError("unknown element: " + kind); 1116 1117 } 1118 accessMap.put(kind, accessValue); 1119 filterMap.put(kind, getFilterSet(accessValue)); 1120 } 1121 } 1122 1123 static EnumSet<AccessKind> getFilterSet(AccessKind acccessValue) { 1124 switch (acccessValue) { 1125 case PUBLIC: 1126 return EnumSet.of(AccessKind.PUBLIC); 1127 case PROTECTED: 1128 default: 1129 return EnumSet.of(AccessKind.PUBLIC, AccessKind.PROTECTED); 1130 case PACKAGE: 1131 return EnumSet.of(AccessKind.PUBLIC, AccessKind.PROTECTED, AccessKind.PACKAGE); 1132 case PRIVATE: 1133 return EnumSet.allOf(AccessKind.class); 1134 } 1135 } 1136 1137 public AccessKind getAccessValue(ElementKind kind) { 1138 if (!ALLOWED_KINDS.contains(kind)) { 1139 throw new IllegalArgumentException("not allowed: " + kind); 1140 } 1141 return accessMap.getOrDefault(kind, AccessKind.PROTECTED); 1142 } 1143 1144 /** 1145 * Returns true if access is allowed. 1146 * 1147 * @param e the element in question 1148 * @return whether the modifiers pass this filter 1149 */ 1150 public boolean checkModifier(Element e) { 1151 Set<Modifier> modifiers = e.getModifiers(); 1152 AccessKind fflag = AccessKind.PACKAGE; 1153 if (modifiers.contains(Modifier.PUBLIC)) { 1154 fflag = AccessKind.PUBLIC; 1155 } else if (modifiers.contains(Modifier.PROTECTED)) { 1156 fflag = AccessKind.PROTECTED; 1157 } else if (modifiers.contains(Modifier.PRIVATE)) { 1158 fflag = AccessKind.PRIVATE; 1159 } 1160 EnumSet<AccessKind> filterSet = filterMap.get(getAllowedKind(e.getKind())); 1161 return filterSet.contains(fflag); 1162 } 1163 1164 // convert a requested element kind to an allowed access kind 1165 private ElementKind getAllowedKind(ElementKind kind) { 1166 switch (kind) { 1167 case CLASS: case METHOD: case MODULE: case PACKAGE: 1168 return kind; 1169 case ANNOTATION_TYPE: case ENUM: case INTERFACE: 1170 return ElementKind.CLASS; 1171 case CONSTRUCTOR: case ENUM_CONSTANT: case EXCEPTION_PARAMETER: 1172 case FIELD: case INSTANCE_INIT: case LOCAL_VARIABLE: case PARAMETER: 1173 case RESOURCE_VARIABLE: case STATIC_INIT: case TYPE_PARAMETER: 1174 return ElementKind.METHOD; 1175 default: 1176 throw new AssertionError("unsupported kind: " + kind); 1177 } 1178 } 1179 } // end ModifierFilter 1180} 1181