001/* 002 * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package org.jdrupes.mdoclet.internal.doclets.toolkit.util; 027 028import javax.lang.model.element.AnnotationMirror; 029import javax.lang.model.element.Element; 030import javax.lang.model.element.ElementKind; 031import javax.lang.model.element.ExecutableElement; 032import javax.lang.model.element.Modifier; 033import javax.lang.model.element.Name; 034import javax.lang.model.element.TypeElement; 035import javax.lang.model.element.VariableElement; 036import javax.lang.model.type.ArrayType; 037import javax.lang.model.type.DeclaredType; 038import javax.lang.model.type.ExecutableType; 039import javax.lang.model.type.TypeKind; 040import javax.lang.model.type.TypeMirror; 041import javax.lang.model.type.WildcardType; 042import javax.lang.model.util.Elements; 043import javax.lang.model.util.SimpleTypeVisitor14; 044 045import org.jdrupes.mdoclet.internal.doclets.toolkit.BaseConfiguration; 046import org.jdrupes.mdoclet.internal.doclets.toolkit.BaseOptions; 047import org.jdrupes.mdoclet.internal.doclets.toolkit.PropertyUtils; 048 049import java.lang.ref.SoftReference; 050import java.util.ArrayList; 051import java.util.Collection; 052import java.util.Collections; 053import java.util.EnumMap; 054import java.util.EnumSet; 055import java.util.HashMap; 056import java.util.HashSet; 057import java.util.LinkedHashMap; 058import java.util.LinkedHashSet; 059import java.util.List; 060import java.util.Map; 061import java.util.Objects; 062import java.util.Set; 063import java.util.function.Predicate; 064import java.util.stream.Collectors; 065import java.util.stream.Stream; 066 067/** 068 * This class computes the main data structure for the doclet's 069 * operations. Essentially, the implementation encapsulating the 070 * javax.lang.model's view of what can be documented about a 071 * type element's members. 072 * <p> 073 * The general operations are as follows: 074 * <p> 075 * Members: these are the members from j.l.m's view but 076 * are structured along the kinds of this class. 077 * <p> 078 * Extra Members: these are members enclosed in an undocumented 079 * package-private type element, and may not be linkable (or documented), 080 * however, the members of such a type element may be documented, as if 081 * declared in the subtype, only if the enclosing type is not being 082 * documented by a filter such as -public, -protected, etc. 083 * <p> 084 * Visible Members: these are the members that are "visible" 085 * and available and should be documented, in a type element. 086 * <p> 087 * The basic rule for computation: when considering a type element, 088 * besides its immediate direct types and interfaces, the computation 089 * should not expand to any other type in the inheritance hierarchy. 090 * <p> 091 * This table generates all the data structures it needs for each 092 * type, as its own view, and will present some form of this to the 093 * doclet as and when required to. 094 */ 095public class VisibleMemberTable { 096 097 public enum Kind { 098 NESTED_CLASSES, 099 ENUM_CONSTANTS, 100 FIELDS, 101 CONSTRUCTORS, 102 METHODS, 103 ANNOTATION_TYPE_MEMBER, 104 ANNOTATION_TYPE_MEMBER_REQUIRED, 105 ANNOTATION_TYPE_MEMBER_OPTIONAL, 106 PROPERTIES; 107 108 private static final EnumSet<Kind> defaultSummarySet = EnumSet.of( 109 NESTED_CLASSES, FIELDS, CONSTRUCTORS, METHODS); 110 private static final EnumSet<Kind> enumSummarySet = EnumSet.of( 111 NESTED_CLASSES, ENUM_CONSTANTS, FIELDS, METHODS); 112 private static final EnumSet<Kind> annotationSummarySet = EnumSet.of( 113 FIELDS, ANNOTATION_TYPE_MEMBER_REQUIRED, 114 ANNOTATION_TYPE_MEMBER_OPTIONAL); 115 private static final EnumSet<Kind> defaultDetailSet = EnumSet.of( 116 FIELDS, CONSTRUCTORS, METHODS); 117 private static final EnumSet<Kind> enumDetailSet = EnumSet.of( 118 ENUM_CONSTANTS, FIELDS, METHODS); 119 private static final EnumSet<Kind> annotationDetailSet = EnumSet.of( 120 FIELDS, ANNOTATION_TYPE_MEMBER); 121 122 /** 123 * {@return the set of possible member kinds for the summaries section of a type element} 124 * @param kind the kind of type element being documented 125 */ 126 public static Set<Kind> forSummariesOf(ElementKind kind) { 127 return switch (kind) { 128 case ANNOTATION_TYPE -> annotationSummarySet; 129 case ENUM -> enumSummarySet; 130 default -> defaultSummarySet; 131 }; 132 } 133 134 /** 135 * {@return the set of possible member kinds for the details section of a type element} 136 * @param kind the kind of type element being documented 137 */ 138 public static Set<Kind> forDetailsOf(ElementKind kind) { 139 return switch (kind) { 140 case ANNOTATION_TYPE -> annotationDetailSet; 141 case ENUM -> enumDetailSet; 142 default -> defaultDetailSet; 143 }; 144 } 145 } 146 147 /** The class or interface described by this table. */ 148 private final TypeElement te; 149 /** 150 * The superclass of {@link #te} or null if {@code te} is an 151 * interface or {@code java.lang.Object}. 152 */ 153 private final TypeElement parent; 154 155 private final BaseConfiguration config; 156 private final BaseOptions options; 157 private final Utils utils; 158 private final VisibleMemberCache mcache; 159 160 /** 161 * Tables for direct and indirect superclasses. 162 * 163 * Tables for superclasses must be unique: no class can appear multiple 164 * times in the inheritance hierarchy for some other class. 165 */ 166 private final Set<VisibleMemberTable> allSuperclasses; 167 /** 168 * Tables for direct and indirect superinterfaces. 169 * 170 * Tables for superinterfaces might not be unique (i.e. an interface 171 * may be added from different lineages). 172 */ 173 private final List<VisibleMemberTable> allSuperinterfaces; 174 /** 175 * Tables for direct superclass and direct superinterfaces. 176 * 177 * The position of a table for the superclass in the list is unspecified. 178 */ 179 private final Set<VisibleMemberTable> parents; 180 181 private Map<Kind, List<Element>> visibleMembers; 182 private final Map<ExecutableElement, PropertyMembers> propertyMap 183 = new HashMap<>(); 184 185 // FIXME: Figure out why it is one-one and not one-to-many. 186 /** 187 * Maps a method m declared in {@code te} to a visible method m' in a 188 * {@code te}'s supertype such that m overrides m'. 189 */ 190 private final Map<ExecutableElement, OverrideInfo> overriddenMethodTable 191 = new LinkedHashMap<>(); 192 193 protected VisibleMemberTable(TypeElement typeElement, 194 BaseConfiguration configuration, 195 VisibleMemberCache mcache) { 196 config = configuration; 197 utils = configuration.utils; 198 options = configuration.getOptions(); 199 te = typeElement; 200 parent = (TypeElement) utils.typeUtils.asElement(te.getSuperclass()); 201 this.mcache = mcache; 202 allSuperclasses = new LinkedHashSet<>(); 203 allSuperinterfaces = new ArrayList<>(); 204 parents = new LinkedHashSet<>(); 205 } 206 207 private void ensureInitialized() { 208 if (visibleMembers != null) 209 return; 210 211 visibleMembers = new EnumMap<>(Kind.class); 212 for (Kind kind : Kind.values()) { 213 visibleMembers.put(kind, new ArrayList<>()); 214 } 215 computeParents(); 216 computeVisibleMembers(); 217 } 218 219 private Set<VisibleMemberTable> getAllSuperclasses() { 220 ensureInitialized(); 221 return allSuperclasses; 222 } 223 224 private List<VisibleMemberTable> getAllSuperinterfaces() { 225 ensureInitialized(); 226 return allSuperinterfaces; 227 } 228 229 /** 230 * Returns a list of all visible enclosed members of a type element, 231 * and inherited members. 232 * <p> 233 * Notes: 234 * a. The list may or may not contain simple overridden methods. 235 * A simple overridden method is one that overrides a super method 236 * with no specification changes as indicated by the existence of a 237 * sole {@code {@inheritDoc}} or devoid of any API comments. 238 * <p> 239 * b.The list may contain (extra) members, inherited by inaccessible 240 * supertypes, primarily package private types. These members are 241 * required to be documented in the subtype when the supertype is 242 * not documented. 243 * 244 * @param kind the member kind 245 * @return a list of all visible members 246 */ 247 public List<Element> getAllVisibleMembers(Kind kind) { 248 ensureInitialized(); 249 return visibleMembers.getOrDefault(kind, List.of()); 250 } 251 252 /** 253 * Returns a list of visible enclosed members of a specified kind, 254 * filtered by the specified predicate. 255 * @param kind the member kind 256 * @param p the predicate used to filter the output 257 * @return a list of visible enclosed members 258 */ 259 public List<Element> getVisibleMembers(Kind kind, Predicate<Element> p) { 260 ensureInitialized(); 261 return visibleMembers.getOrDefault(kind, List.of()).stream() 262 .filter(p) 263 .toList(); 264 } 265 266 /** 267 * Returns a list of all enclosed members including any extra members. 268 * Typically called by various builders. 269 * 270 * @param kind the member kind 271 * @return a list of visible enclosed members 272 */ 273 public List<Element> getVisibleMembers(Kind kind) { 274 Predicate<Element> declaredAndLeafMembers = e -> { 275 TypeElement encl = utils.getEnclosingTypeElement(e); 276 return Objects.equals(encl, te) 277 || utils.isUndocumentedEnclosure(encl); 278 }; 279 return getVisibleMembers(kind, declaredAndLeafMembers); 280 } 281 282 /** 283 * Returns a list of visible enclosed members of given kind, 284 * declared in this type element, and does not include 285 * any inherited members or extra members. 286 * 287 * @return a list of visible enclosed members in this type 288 */ 289 public List<Element> getMembers(Kind kind) { 290 Predicate<Element> onlyLocallyDeclaredMembers 291 = e -> Objects.equals(utils.getEnclosingTypeElement(e), te); 292 return getVisibleMembers(kind, onlyLocallyDeclaredMembers); 293 } 294 295 /** 296 * Returns the method overridden by the provided method, or {@code null}. 297 * 298 * Sometimes it's not possible to link to a method that a link, linkplain, 299 * or see tag mentions. This is because the method is a "simple override" 300 * and, thus, has no useful documentation, or because the method is 301 * declared in a type that has package access and, thus, has no visible 302 * documentation. 303 * 304 * Call this method to determine if any of the above is the case. If the 305 * call returns a method element, link to that method element instead of 306 * the provided method. 307 * 308 * @param e the method to check 309 * @return the method found or {@code null} 310 */ 311 public ExecutableElement getOverriddenMethod(ExecutableElement e) { 312 // TODO: consider possible ambiguities: multiple overridden methods 313 ensureInitialized(); 314 assert !overriddenMethodTable.containsKey(null); 315 OverrideInfo found = overriddenMethodTable.get(e); 316 if (found != null 317 && (found.simpleOverride || utils 318 .isUndocumentedEnclosure(utils.getEnclosingTypeElement(e)))) { 319 return found.overriddenMethod; 320 } 321 return null; 322 } 323 324 /** 325 * {@return true if the specified method is NOT a simple override of some 326 * other method, otherwise false} 327 * 328 * @param e the method to check 329 */ 330 private boolean isNotSimpleOverride(ExecutableElement e) { 331 ensureInitialized(); 332 333 var info = overriddenMethodTable.get(e); 334 return info == null || !info.simpleOverride; 335 } 336 337 /** 338 * Returns a set of visible type elements in this type element's lineage. 339 * <p> 340 * This method returns the supertypes in the inheritance 341 * order C, B, A, j.l.O. The superinterfaces however are 342 * alpha sorted and appended to the resulting set. 343 * 344 * @return the set of visible classes in this map 345 */ 346 public Set<TypeElement> getVisibleTypeElements() { 347 ensureInitialized(); 348 Set<TypeElement> result = new LinkedHashSet<>(); 349 350 // Add this type element first. 351 result.add(te); 352 353 // Add the superclasses. 354 allSuperclasses.stream() 355 .map(vmt -> vmt.te) 356 .forEach(result::add); 357 358 // ... and finally the sorted superinterfaces. 359 allSuperinterfaces.stream() 360 .map(vmt -> vmt.te) 361 .sorted(utils.comparators.makeGeneralPurposeComparator()) 362 .forEach(result::add); 363 364 return result; 365 } 366 367 /** 368 * Returns true if this table contains visible members of 369 * any kind, including inherited members. 370 * 371 * @return true if visible members are present. 372 */ 373 public boolean hasVisibleMembers() { 374 for (Kind kind : Kind.values()) { 375 if (hasVisibleMembers(kind)) 376 return true; 377 } 378 return false; 379 } 380 381 /** 382 * Returns true if this table contains visible members of 383 * the specified kind, including inherited members. 384 * 385 * @return true if visible members are present. 386 */ 387 public boolean hasVisibleMembers(Kind kind) { 388 ensureInitialized(); 389 List<Element> elements = visibleMembers.get(kind); 390 return elements != null && !elements.isEmpty(); 391 } 392 393 /** 394 * Returns the field for a property identified by any of the methods 395 * for that property. 396 * 397 * @param ee the method 398 * @return the field or null if absent 399 */ 400 public VariableElement getPropertyField(ExecutableElement ee) { 401 ensureInitialized(); 402 PropertyMembers pm = propertyMap.get(ee); 403 return pm == null ? null : pm.field; 404 } 405 406 /** 407 * Returns the getter method for a property identified by any of the methods 408 * for that property. 409 * 410 * @param ee the method 411 * @return the getter or null if absent 412 */ 413 public ExecutableElement getPropertyGetter(ExecutableElement ee) { 414 ensureInitialized(); 415 PropertyMembers pm = propertyMap.get(ee); 416 return pm == null ? null : pm.getter; 417 } 418 419 /** 420 * Returns the setter method for a property identified by any of the methods 421 * for that property. 422 * 423 * @param ee the method 424 * @return the setter or null if absent 425 */ 426 public ExecutableElement getPropertySetter(ExecutableElement ee) { 427 ensureInitialized(); 428 PropertyMembers pm = propertyMap.get(ee); 429 return pm == null ? null : pm.setter; 430 } 431 432 /** 433 * Returns the property method for a property identified by any of the methods 434 * for that property. 435 * 436 * @param ee the method 437 * @return the property method or null if absent 438 */ 439 public ExecutableElement getPropertyMethod(ExecutableElement ee) { 440 ensureInitialized(); 441 PropertyMembers pm = propertyMap.get(ee); 442 return pm == null ? null : pm.propertyMethod; 443 } 444 445 private void computeParents() { 446 // suppress parents of annotation interfaces 447 if (utils.isAnnotationInterface(te)) { 448 return; 449 } 450 451 for (TypeMirror intfType : te.getInterfaces()) { 452 TypeElement intfc = utils.asTypeElement(intfType); 453 if (intfc != null) { 454 VisibleMemberTable vmt = mcache.getVisibleMemberTable(intfc); 455 allSuperinterfaces.add(vmt); 456 boolean added = parents.add(vmt); 457 assert added; // no duplicates 458 allSuperinterfaces.addAll(vmt.getAllSuperinterfaces()); 459 } 460 } 461 462 if (parent != null) { 463 VisibleMemberTable vmt = mcache.getVisibleMemberTable(parent); 464 allSuperclasses.add(vmt); 465 assert Collections.disjoint(allSuperclasses, 466 vmt.getAllSuperclasses()); // no duplicates 467 allSuperclasses.addAll(vmt.getAllSuperclasses()); 468 // Add direct and indirect superinterfaces of a superclass. 469 allSuperinterfaces.addAll(vmt.getAllSuperinterfaces()); 470 boolean added = parents.add(vmt); 471 assert added; // no duplicates 472 } 473 } 474 475 private void computeVisibleMembers() { 476 477 // Note: these have some baggage, and are redundant, 478 // allow this to be GC'ed. 479 LocalMemberTable lmt = new LocalMemberTable(); 480 481 for (Kind k : Kind.values()) { 482 computeVisibleMembers(lmt, k); 483 } 484 // All members have been computed, compute properties. 485 computeVisibleProperties(lmt); 486 } 487 488 void computeVisibleMembers(LocalMemberTable lmt, Kind kind) { 489 switch (kind) { 490 case FIELDS: 491 case NESTED_CLASSES: 492 computeVisibleFieldsAndInnerClasses(lmt, kind); 493 return; 494 495 case METHODS: 496 computeVisibleMethods(lmt); 497 return; 498 499 // Defer properties related computations for later. 500 case PROPERTIES: 501 return; 502 503 default: 504 List<Element> list = lmt.getOrderedMembers(kind).stream() 505 .filter(this::mustDocument) 506 .toList(); 507 visibleMembers.put(kind, list); 508 break; 509 } 510 } 511 512 private boolean mustDocument(Element e) { 513 // these checks are ordered in a particular way to avoid parsing unless 514 // absolutely necessary 515 return utils.shouldDocument(e) && !utils.hasHiddenTag(e); 516 } 517 518 private boolean allowInheritedMembers(Element e, Kind kind, 519 LocalMemberTable lmt) { 520 return isAccessible(e) && !isMemberHidden(e, kind, lmt); 521 } 522 523 private boolean isAccessible(Element e) { 524 if (utils.isPrivate(e)) 525 return false; 526 527 if (utils.isPackagePrivate(e)) 528 // Allowed iff this type-element is in the same package as the 529 // element 530 return utils.containingPackage(e) 531 .equals(utils.containingPackage(te)); 532 533 return true; 534 } 535 536 private boolean isMemberHidden(Element inheritedMember, Kind kind, 537 LocalMemberTable lmt) { 538 Elements elementUtils = config.docEnv.getElementUtils(); 539 switch (kind) { 540 default: 541 List<Element> list 542 = lmt.getMembers(inheritedMember.getSimpleName(), kind); 543 if (list.isEmpty()) 544 return false; 545 return elementUtils.hides(list.get(0), inheritedMember); 546 case METHODS: 547 case CONSTRUCTORS: // Handled elsewhere. 548 throw new IllegalArgumentException("incorrect kind"); 549 } 550 } 551 552 private void computeVisibleFieldsAndInnerClasses(LocalMemberTable lmt, 553 Kind kind) { 554 Set<Element> result = new LinkedHashSet<>(); 555 for (VisibleMemberTable pvmt : parents) { 556 result.addAll(pvmt.getAllVisibleMembers(kind)); 557 } 558 559 // Filter out members in the inherited list that are hidden 560 // by this type or should not be inherited at all. 561 Stream<Element> inheritedStream = result.stream() 562 .filter(e -> allowInheritedMembers(e, kind, lmt)); 563 564 // Filter out elements that should not be documented 565 // Prefix local results first 566 List<Element> list = Stream 567 .concat(lmt.getOrderedMembers(kind).stream(), inheritedStream) 568 .filter(this::mustDocument) 569 .toList(); 570 571 visibleMembers.put(kind, list); 572 } 573 574 // This method computes data structures related to method members 575 // of a class or an interface. 576 // 577 // TODO The computation is performed manually, by applying JLS rules. 578 // While jdk.javadoc does need custom and specialized data structures, 579 // this method does not feel DRY. It should be possible to improve 580 // it by delegating some, if not most, of the JLS wrestling to 581 // javax.lang.model. For example, while it cannot help us get the 582 // structures, such as overriddenMethodTable, javax.lang.model can 583 // help us get all method members of a class or an interface t by calling 584 // ElementFilter.methodsIn(Elements.getAllMembers(t)). 585 private void computeVisibleMethods(LocalMemberTable lmt) { 586 // parentMethods is a union of visible methods from all parents. 587 // It is used to figure out which methods this type should inherit. 588 // Inherited methods are those parent methods that remain after all 589 // methods that cannot be inherited are eliminated. 590 Set<Element> parentMethods = new LinkedHashSet<>(); 591 for (var p : parents) { 592 // Lists of visible methods from different parents may share some 593 // methods. These are the methods that the parents inherited from 594 // their common ancestor. 595 // 596 // Such methods won't result in duplicates in parentMethods as we 597 // purposefully don't track duplicates. 598 // FIXME: add a test to assert the order (LinkedHashSet) 599 parentMethods.addAll(p.getAllVisibleMembers(Kind.METHODS)); 600 } 601 602 // overriddenByTable maps an ancestor (grandparent and above) method 603 // to parent methods that override it: 604 // 605 // key 606 // : a method overridden by one or more parent methods 607 // value 608 // : a list of parent methods that override the key 609 Map<ExecutableElement, List<ExecutableElement>> overriddenByTable 610 = new HashMap<>(); 611 for (var p : parents) { 612 // Merge the lineage overrides into local table 613 p.overriddenMethodTable.forEach((method, methodInfo) -> { 614 if (!methodInfo.simpleOverride) { // consider only real 615 // overrides 616 var list = overriddenByTable.computeIfAbsent( 617 methodInfo.overriddenMethod, 618 k -> new ArrayList<>()); 619 list.add(method); 620 } 621 }); 622 } 623 624 // filter out methods that aren't inherited 625 // 626 // nb. This statement has side effects that can initialize 627 // members of the overriddenMethodTable field, so it must be 628 // evaluated eagerly with toList(). 629 List<Element> inheritedMethods = parentMethods.stream() 630 .filter(e -> allowInheritedMethod((ExecutableElement) e, 631 overriddenByTable, lmt)) 632 .toList(); 633 634 // filter out "simple overrides" from local methods 635 Predicate<ExecutableElement> nonSimpleOverride = m -> { 636 OverrideInfo i = overriddenMethodTable.get(m); 637 return i == null || !i.simpleOverride; 638 }; 639 640 Stream<ExecutableElement> localStream 641 = lmt.getOrderedMembers(Kind.METHODS) 642 .stream() 643 .map(m -> (ExecutableElement) m) 644 .filter(nonSimpleOverride); 645 646 // Merge the above list and stream, making sure the local methods 647 // precede the others 648 // Final filtration of elements 649 // FIXME add a test to assert the order or remove that part of the 650 // comment above ^ 651 List<Element> list 652 = Stream.concat(localStream, inheritedMethods.stream()) 653 .filter(this::mustDocument) 654 .toList(); 655 656 visibleMembers.put(Kind.METHODS, list); 657 658 // copy over overridden tables from the lineage 659 for (VisibleMemberTable pvmt : parents) { 660 // a key in overriddenMethodTable is a method _declared_ in the 661 // respective parent; 662 // no two _different_ parents can share a declared method, by 663 // definition; 664 // if parents in the list are different (i.e. the list of parents 665 // doesn't contain duplicates), 666 // then no keys are equal and thus no replace happens 667 // if the list of parents contains duplicates, values for the equal 668 // keys are equal, 669 // so no harm if they are replaced in the map 670 assert putAllIsNonReplacing(overriddenMethodTable, 671 pvmt.overriddenMethodTable); 672 overriddenMethodTable.putAll(pvmt.overriddenMethodTable); 673 } 674 } 675 676 private static <K, V> boolean putAllIsNonReplacing(Map<K, V> dst, 677 Map<K, V> src) { 678 for (var e : src.entrySet()) { 679 if (dst.containsKey(e.getKey()) 680 && !Objects.equals(dst.get(e.getKey()), e.getValue())) { 681 return false; 682 } 683 } 684 return true; 685 } 686 687 private boolean allowInheritedMethod(ExecutableElement inheritedMethod, 688 Map<ExecutableElement, List<ExecutableElement>> overriddenByTable, 689 LocalMemberTable lmt) { 690 // JLS 8.4.8: A class does not inherit private or static methods from 691 // its superinterface types. 692 // 693 // JLS 9.4.1: An interface does not inherit private or static methods 694 // from its superinterfaces. 695 // 696 // JLS 8.4.8: m is public, protected, or declared with package access 697 // in the same package as C 698 // 699 // JLS 9.4: A method in the body of an interface declaration may be 700 // declared public or private. If no access modifier is given, the 701 // method is implicitly public. 702 if (!isAccessible(inheritedMethod)) 703 return false; 704 705 final boolean haveStatic = utils.isStatic(inheritedMethod); 706 final boolean inInterface = isDeclaredInInterface(inheritedMethod); 707 708 // Static interface methods are never inherited (JLS 8.4.8 and 9.1.3) 709 if (haveStatic && inInterface) { 710 return false; 711 } 712 713 // Multiple-Inheritance: remove the interface method that may have 714 // been overridden by another interface method in the hierarchy 715 // 716 // Note: The following approach is very simplistic and is compatible 717 // with old VMM. A future enhancement, may include a contention breaker, 718 // to correctly eliminate those methods that are merely definitions 719 // in favor of concrete overriding methods, for instance those that have 720 // API documentation and are not abstract OR default methods. 721 if (inInterface) { 722 List<ExecutableElement> list 723 = overriddenByTable.get(inheritedMethod); 724 if (list != null) { 725 boolean found = list.stream() 726 .anyMatch(this::isDeclaredInInterface); 727 if (found) 728 return false; 729 } 730 } 731 732 Elements elementUtils = config.docEnv.getElementUtils(); 733 734 // Check the local methods in this type. 735 // List contains overloads and probably something else, but one match is 736 // enough, hence short-circuiting 737 List<Element> lMethods 738 = lmt.getMembers(inheritedMethod.getSimpleName(), Kind.METHODS); 739 for (Element le : lMethods) { 740 ExecutableElement lMethod = (ExecutableElement) le; 741 // Ignore private methods or those methods marked with 742 // a "hidden" tag. // FIXME I cannot see where @hidden is ignored 743 if (utils.isPrivate(lMethod)) 744 continue; 745 746 // Remove methods that are "hidden", in JLS terms. 747 if (haveStatic && utils.isStatic(lMethod) && 748 elementUtils.hides(lMethod, inheritedMethod)) { 749 return false; 750 } 751 752 // Check for overriding methods. 753 if (elementUtils.overrides(lMethod, inheritedMethod, 754 utils.getEnclosingTypeElement(lMethod))) { 755 756 assert utils.getEnclosingTypeElement(lMethod).equals(te); 757 758 // Disallow package-private super methods to leak in 759 TypeElement encl 760 = utils.getEnclosingTypeElement(inheritedMethod); 761 if (utils.isUndocumentedEnclosure(encl)) { 762 // FIXME 763 // is simpleOverride=false here to force to be used because 764 // it cannot be linked to, because package-private? 765 overriddenMethodTable.computeIfAbsent(lMethod, 766 l -> new OverrideInfo(inheritedMethod, false)); 767 return false; 768 } 769 770 // Even with --override-methods=summary we want to include 771 // details of 772 // overriding method if something noteworthy has been added or 773 // changed 774 // either in the local overriding method or an in-between 775 // overriding method 776 // (as evidenced by an entry in overriddenByTable). 777 boolean simpleOverride = utils.isSimpleOverride(lMethod) 778 && !overridingSignatureChanged(lMethod, inheritedMethod) 779 && !overriddenByTable.containsKey(inheritedMethod); 780 overriddenMethodTable.computeIfAbsent(lMethod, 781 l -> new OverrideInfo(inheritedMethod, simpleOverride)); 782 return simpleOverride; 783 } 784 } 785 return true; 786 } 787 788 private boolean isDeclaredInInterface(ExecutableElement e) { 789 return e.getEnclosingElement().getKind() == ElementKind.INTERFACE; 790 } 791 792 // Check whether the signature of an overriding method has any changes worth 793 // being documented compared to the overridden method. 794 private boolean overridingSignatureChanged(ExecutableElement method, 795 ExecutableElement overriddenMethod) { 796 // Covariant return type 797 TypeMirror overriddenMethodReturn = overriddenMethod.getReturnType(); 798 TypeMirror methodReturn = method.getReturnType(); 799 if (methodReturn.getKind() == TypeKind.DECLARED 800 && overriddenMethodReturn.getKind() == TypeKind.DECLARED 801 && !utils.typeUtils.isSameType(methodReturn, overriddenMethodReturn) 802 && utils.typeUtils.isSubtype(methodReturn, 803 overriddenMethodReturn)) { 804 return true; 805 } 806 // Modifiers changed from protected to public, non-final to final, or 807 // change in abstractness 808 Set<Modifier> modifiers = method.getModifiers(); 809 Set<Modifier> overriddenModifiers = overriddenMethod.getModifiers(); 810 if ((modifiers.contains(Modifier.PUBLIC) 811 && overriddenModifiers.contains(Modifier.PROTECTED)) 812 || modifiers.contains(Modifier.FINAL) 813 || modifiers.contains(Modifier.ABSTRACT) != overriddenModifiers 814 .contains(Modifier.ABSTRACT)) { 815 return true; 816 } 817 // Change in thrown types 818 if (!method.getThrownTypes() 819 .equals(overriddenMethod.getThrownTypes())) { 820 return true; 821 } 822 // Documented annotations added anywhere in the method signature 823 return !getDocumentedAnnotations(method) 824 .equals(getDocumentedAnnotations(overriddenMethod)); 825 } 826 827 private Set<AnnotationMirror> 828 getDocumentedAnnotations(ExecutableElement element) { 829 Set<AnnotationMirror> annotations = new HashSet<>(); 830 addDocumentedAnnotations(annotations, element.getAnnotationMirrors()); 831 832 new SimpleTypeVisitor14<Void, Void>() { 833 @Override 834 protected Void defaultAction(TypeMirror e, Void v) { 835 addDocumentedAnnotations(annotations, e.getAnnotationMirrors()); 836 return null; 837 } 838 839 @Override 840 public Void visitArray(ArrayType t, Void unused) { 841 if (t.getComponentType() != null) { 842 visit(t.getComponentType()); 843 } 844 return super.visitArray(t, unused); 845 } 846 847 @Override 848 public Void visitDeclared(DeclaredType t, Void unused) { 849 t.getTypeArguments().forEach(this::visit); 850 return super.visitDeclared(t, unused); 851 } 852 853 @Override 854 public Void visitWildcard(WildcardType t, Void unused) { 855 if (t.getExtendsBound() != null) { 856 visit(t.getExtendsBound()); 857 } 858 if (t.getSuperBound() != null) { 859 visit(t.getSuperBound()); 860 } 861 return super.visitWildcard(t, unused); 862 } 863 864 @Override 865 public Void visitExecutable(ExecutableType t, Void unused) { 866 t.getParameterTypes().forEach(this::visit); 867 t.getTypeVariables().forEach(this::visit); 868 visit(t.getReturnType()); 869 return super.visitExecutable(t, unused); 870 } 871 }.visit(element.asType()); 872 873 return annotations; 874 } 875 876 private void addDocumentedAnnotations(Set<AnnotationMirror> annotations, 877 List<? extends AnnotationMirror> annotationMirrors) { 878 annotationMirrors.forEach(annotation -> { 879 if (utils.isDocumentedAnnotation( 880 (TypeElement) annotation.getAnnotationType().asElement())) { 881 annotations.add(annotation); 882 } 883 }); 884 } 885 886 /* 887 * A container of members declared in this class or interface. Members of 888 * the same kind stored in declaration order. The container supports 889 * efficient lookup by a member's simple name. 890 */ 891 private class LocalMemberTable { 892 893 final Map<Kind, List<Element>> orderedMembers 894 = new EnumMap<>(Kind.class); 895 final Map<Kind, Map<Name, List<Element>>> namedMembers 896 = new EnumMap<>(Kind.class); 897 898 LocalMemberTable() { 899 // elements directly declared by this class or interface, 900 // listed in declaration order 901 List<? extends Element> elements = te.getEnclosedElements(); 902 for (Element e : elements) { 903 if (options.noDeprecated() && utils.isDeprecated(e)) { 904 continue; 905 } 906 switch (e.getKind()) { 907 case CLASS: 908 case INTERFACE: 909 case ENUM: 910 case ANNOTATION_TYPE: 911 case RECORD: 912 addMember(e, Kind.NESTED_CLASSES); 913 break; 914 case FIELD: 915 addMember(e, Kind.FIELDS); 916 break; 917 case ENUM_CONSTANT: 918 addMember(e, Kind.ENUM_CONSTANTS); 919 break; 920 case METHOD: 921 if (utils.isAnnotationInterface(te)) { 922 addMember(e, Kind.ANNOTATION_TYPE_MEMBER); 923 addMember(e, 924 ((ExecutableElement) e).getDefaultValue() == null 925 ? Kind.ANNOTATION_TYPE_MEMBER_REQUIRED 926 : Kind.ANNOTATION_TYPE_MEMBER_OPTIONAL); 927 } else { 928 addMember(e, Kind.METHODS); 929 } 930 break; 931 case CONSTRUCTOR: 932 addMember(e, Kind.CONSTRUCTORS); 933 break; 934 } 935 } 936 937 // protect element lists from unintended changes by clients 938 orderedMembers.replaceAll(this::sealList); 939 namedMembers.values().forEach(m -> m.replaceAll(this::sealList)); 940 } 941 942 private <K, V> List<V> sealList(K unusedKey, List<V> v) { 943 return Collections.unmodifiableList(v); 944 } 945 946 void addMember(Element e, Kind kind) { 947 orderedMembers.computeIfAbsent(kind, k -> new ArrayList<>()).add(e); 948 namedMembers.computeIfAbsent(kind, k -> new HashMap<>()) 949 .computeIfAbsent(e.getSimpleName(), l -> new ArrayList<>()) 950 .add(e); 951 } 952 953 List<Element> getOrderedMembers(Kind kind) { 954 return orderedMembers.getOrDefault(kind, List.of()); 955 } 956 957 List<Element> getMembers(Name simpleName, Kind kind) { 958 return namedMembers.getOrDefault(kind, Map.of()) 959 .getOrDefault(simpleName, List.of()); 960 } 961 962 <T extends Element> List<T> getMembers(Name simpleName, Kind kind, 963 Class<T> clazz) { 964 return getMembers(simpleName, kind) 965 .stream() 966 .map(clazz::cast) 967 .toList(); 968 } 969 970 List<ExecutableElement> getPropertyMethods(Name simpleName) { 971 return getMembers(simpleName, Kind.METHODS).stream() 972 .filter(m -> (utils.isPublic(m) || utils.isProtected(m))) 973 .map(m -> (ExecutableElement) m) 974 .toList(); 975 } 976 } 977 978 private record PropertyMembers(ExecutableElement propertyMethod, 979 VariableElement field, 980 ExecutableElement getter, ExecutableElement setter) { 981 } 982 983 /* 984 * JavaFX convention notes. 985 * A JavaFX property-method is a method, which ends with "Property" in 986 * its name, takes no parameters and typically returns a subtype of 987 * javafx.beans. 988 * ReadOnlyProperty, in the strictest sense. However, it may not always 989 * be possible for the doclet to have access to j.b.ReadOnlyProperty, 990 * for this reason the strict check is disabled via an undocumented flag. 991 * 992 * Note, a method should not be considered as a property-method, 993 * if it satisfied the previously stated conditions AND if the 994 * method begins with "set", "get" or "is". 995 * 996 * Supposing we have {@code BooleanProperty acmeProperty()}, then the 997 * property-name is "acme". 998 * 999 * Property field, one may or may not exist and could be private, and 1000 * should match the property-method. 1001 * 1002 * A property-setter is a method starting with "set", and the 1003 * first character of the upper-cased starting character of the property 1004 * name, the 1005 * method must take 1 argument and must return a <code>void</code>. 1006 * 1007 * Using the above example {@code void setAcme(Something s)} can be 1008 * considered as a property-setter of the property "acme". 1009 * 1010 * A property-getter is a method starting with "get" and the first character 1011 * upper-cased property-name, having no parameters. A method that does not 1012 * take any 1013 * parameters and starting with "is" and an upper-cased property-name, 1014 * returning a primitive type boolean or BooleanProperty can also be 1015 * considered as a getter, however there must be only one getter for every 1016 * property. 1017 * 1018 * For example {@code Object getAcme()} is a property-getter, and 1019 * {@code boolean isFoo()} 1020 */ 1021 private void computeVisibleProperties(LocalMemberTable lmt) { 1022 if (!options.javafx()) 1023 return; 1024 1025 PropertyUtils pUtils = config.propertyUtils; 1026 List<Element> list 1027 = visibleMembers.getOrDefault(Kind.METHODS, List.of()) 1028 .stream() 1029 .filter(e -> pUtils.isPropertyMethod((ExecutableElement) e)) 1030 .toList(); 1031 1032 visibleMembers.put(Kind.PROPERTIES, list); 1033 1034 List<ExecutableElement> propertyMethods = list.stream() 1035 .map(e -> (ExecutableElement) e) 1036 .filter(e -> Objects.equals(utils.getEnclosingTypeElement(e), te)) 1037 .toList(); 1038 1039 // Compute additional properties related sundries. 1040 for (ExecutableElement propertyMethod : propertyMethods) { 1041 String baseName = pUtils.getBaseName(propertyMethod); 1042 List<VariableElement> flist 1043 = lmt.getMembers(utils.elementUtils.getName(baseName), 1044 Kind.FIELDS, VariableElement.class); 1045 VariableElement field = flist.isEmpty() ? null : flist.get(0); 1046 1047 // TODO: this code does not seem to be covered by tests well 1048 // (JDK-8304170) 1049 ExecutableElement getter = null; 1050 var g = lmt 1051 .getPropertyMethods(utils.elementUtils 1052 .getName(pUtils.getGetName(propertyMethod))) 1053 .stream() 1054 .filter(m -> m.getParameters().isEmpty()) // Getters have zero 1055 // params, no 1056 // overloads! 1057 .findAny(); 1058 if (g.isPresent()) { 1059 getter = g.get(); 1060 } else { 1061 // Check if isProperty methods are present ? 1062 var i = lmt 1063 .getPropertyMethods(utils.elementUtils 1064 .getName(pUtils.getIsName(propertyMethod))) 1065 .stream() 1066 .filter(m -> m.getParameters().isEmpty()) 1067 .findAny(); 1068 if (i.isPresent()) { 1069 // Check if the return type of property method matches an 1070 // isProperty method. 1071 if (pUtils.hasIsMethod(propertyMethod)) { 1072 // Getters have zero params, no overloads! 1073 getter = i.get(); 1074 } 1075 } 1076 } 1077 1078 var setter = lmt 1079 .getPropertyMethods(utils.elementUtils 1080 .getName(pUtils.getSetName(propertyMethod))) 1081 .stream() 1082 // TODO: the number and the types of parameters a setter takes 1083 // is not tested (JDK-8304170) 1084 .filter(m -> m.getParameters().size() == 1 1085 && pUtils.isValidSetterMethod(m)) 1086 .findAny() 1087 .orElse(null); 1088 1089 PropertyMembers pm 1090 = new PropertyMembers(propertyMethod, field, getter, setter); 1091 propertyMap.put(propertyMethod, pm); 1092 if (getter != null) { 1093 propertyMap.put(getter, pm); 1094 } 1095 if (setter != null) { 1096 propertyMap.put(setter, pm); 1097 } 1098 1099 // Debugging purposes 1100 // System.out.println("te: " + te + ": " + 1101 // utils.getEnclosingTypeElement(propertyMethod) + 1102 // ":" + propertyMethod.toString() + "->" + 1103 // propertyMap.get(propertyMethod)); 1104 } 1105 } 1106 1107 // Future cleanups 1108 1109 private final Map<ExecutableElement, 1110 SoftReference<ImplementedMethods>> implementMethodsFinders 1111 = new HashMap<>(); 1112 1113 private ImplementedMethods 1114 getImplementedMethodsFinder(ExecutableElement method) { 1115 SoftReference<ImplementedMethods> ref 1116 = implementMethodsFinders.get(method); 1117 ImplementedMethods imf = ref == null ? null : ref.get(); 1118 // imf does not exist or was gc'ed away? 1119 if (imf == null) { 1120 imf = new ImplementedMethods(method); 1121 implementMethodsFinders.put(method, new SoftReference<>(imf)); 1122 } 1123 return imf; 1124 } 1125 1126 public List<ExecutableElement> 1127 getImplementedMethods(ExecutableElement method) { 1128 ImplementedMethods imf = getImplementedMethodsFinder(method); 1129 return imf.getImplementedMethods().stream() 1130 .filter(this::isNotSimpleOverride) 1131 .toList(); 1132 } 1133 1134 public TypeMirror getImplementedMethodHolder(ExecutableElement method, 1135 ExecutableElement implementedMethod) { 1136 ImplementedMethods imf = getImplementedMethodsFinder(method); 1137 return imf.getMethodHolder(implementedMethod); 1138 } 1139 1140 private class ImplementedMethods { 1141 1142 private final Map<ExecutableElement, TypeMirror> interfaces 1143 = new LinkedHashMap<>(); 1144 1145 public ImplementedMethods(ExecutableElement implementer) { 1146 var typeElement = (TypeElement) implementer.getEnclosingElement(); 1147 for (TypeMirror i : utils.getAllInterfaces(typeElement)) { 1148 TypeElement dst = utils.asTypeElement(i); // a type element to 1149 // look an implemented 1150 // method in 1151 ExecutableElement implemented 1152 = findImplementedMethod(dst, implementer); 1153 if (implemented == null) { 1154 continue; 1155 } 1156 var prev = interfaces.put(implemented, i); 1157 // no two type elements declare the same method 1158 assert prev == null; 1159 // dst can be generic, while i might be parameterized; but they 1160 // must the same type element. For example, if dst is Set<T>, 1161 // then i is Set<String> 1162 assert Objects.equals(((DeclaredType) i).asElement(), dst); 1163 } 1164 } 1165 1166 private ExecutableElement findImplementedMethod(TypeElement te, 1167 ExecutableElement implementer) { 1168 var typeElement = (TypeElement) implementer.getEnclosingElement(); 1169 for (var m : utils.getMethods(te)) { 1170 if (utils.elementUtils.overrides(implementer, m, typeElement)) { 1171 return m; 1172 } 1173 } 1174 return null; 1175 } 1176 1177 /** 1178 * Returns a collection of interface methods which the method passed in the 1179 * constructor is implementing. The search/build order is as follows: 1180 * <pre> 1181 * 1. Search in all the immediate interfaces which this method's class is 1182 * implementing. Do it recursively for the superinterfaces as well. 1183 * 2. Traverse all the superclasses and search recursively in the 1184 * interfaces which those superclasses implement. 1185 * </pre> 1186 * 1187 * @return a collection of implemented methods 1188 */ 1189 Collection<ExecutableElement> getImplementedMethods() { 1190 return interfaces.keySet(); 1191 } 1192 1193 TypeMirror getMethodHolder(ExecutableElement ee) { 1194 return interfaces.get(ee); 1195 } 1196 } 1197 1198 /* 1199 * (Here "override" used as a noun, not a verb, for a short and descriptive 1200 * name. Sadly, we cannot use "Override" as a complete name because a clash 1201 * with @java.lang.Override would make it inconvenient.) 1202 * 1203 * Used to provide additional attributes to the otherwise boolean 1204 * "overrides(a, b)" relationship. 1205 * 1206 * Overriding method could be a key in a map and an instance of this 1207 * record could be the value. 1208 */ 1209 private record OverrideInfo(ExecutableElement overriddenMethod, 1210 boolean simpleOverride) { 1211 @Override // for debugging 1212 public String toString() { 1213 return overriddenMethod.getEnclosingElement() 1214 + "::" + overriddenMethod + ", simple=" + simpleOverride; 1215 } 1216 } 1217 1218 @Override 1219 public int hashCode() { 1220 return te.hashCode(); 1221 } 1222 1223 @Override 1224 public boolean equals(Object obj) { 1225 if (!(obj instanceof VisibleMemberTable other)) 1226 return false; 1227 return te.equals(other.te); 1228 } 1229}