1 /* 2 * Copyright (c) 2018, 2019, 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 */ 25 26 package jdk.javadoc.internal.doclets.toolkit.util; 27 28 import javax.lang.model.element.Element; 29 import javax.lang.model.element.ExecutableElement; 30 import javax.lang.model.element.TypeElement; 31 import javax.lang.model.element.VariableElement; 32 import javax.lang.model.type.TypeKind; 33 import javax.lang.model.type.TypeMirror; 34 import javax.lang.model.util.Elements; 35 import javax.lang.model.util.SimpleElementVisitor14; 36 import java.lang.ref.SoftReference; 37 import java.util.ArrayList; 38 import java.util.Collections; 39 import java.util.EnumMap; 40 import java.util.EnumSet; 41 import java.util.HashMap; 42 import java.util.LinkedHashMap; 43 import java.util.LinkedHashSet; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Set; 47 import java.util.function.Predicate; 48 import java.util.stream.Collectors; 49 50 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; 51 import jdk.javadoc.internal.doclets.toolkit.BaseOptions; 52 import jdk.javadoc.internal.doclets.toolkit.PropertyUtils; 53 54 /** 55 * This class computes the main data structure for the doclet's 56 * operations. Essentially, the implementation encapsulating the 57 * javax.lang.models view of what can be documented about a 58 * type element's members. 59 * <p> 60 * The general operations are as follows: 61 * <p> 62 * Members: these are the members from jx.l.m's view but 63 * are structured along the kinds of this class. 64 * <p> 65 * Extra Members: these are members enclosed in an undocumented 66 * package-private type element, and may not be linkable (or documented), 67 * however, the members of such a type element may be documented, as if 68 * declared in the sub type, only if the enclosing type is not being 69 * documented by a filter such as -public, -protected, etc. 70 * <p> 71 * Visible Members: these are the members that are "visible" 72 * and available and should be documented, in a type element. 73 * <p> 74 * The basic rule for computation: when considering a type element, 75 * besides its immediate direct types and interfaces, the computation 76 * should not expand to any other type in the inheritance hierarchy. 77 * <p> 78 * This table generates all the data structures it needs for each 79 * type, as its own view, and will present some form of this to the 80 * doclet as and when required to. 81 * 82 * <p><b>This is NOT part of any supported API. 83 * If you write code that depends on this, you do so at your own risk. 84 * This code and its internal interfaces are subject to change or 85 * deletion without notice.</b> 86 * 87 */ 88 89 public class VisibleMemberTable { 90 91 public enum Kind { 92 INNER_CLASSES, 93 ENUM_CONSTANTS, 94 FIELDS, 95 CONSTRUCTORS, 96 METHODS, 97 ANNOTATION_TYPE_FIELDS, 98 ANNOTATION_TYPE_MEMBER_OPTIONAL, 99 ANNOTATION_TYPE_MEMBER_REQUIRED, 100 PROPERTIES; 101 102 public static final EnumSet<Kind> summarySet = EnumSet.range(INNER_CLASSES, METHODS); 103 public static final EnumSet<Kind> detailSet = EnumSet.range(ENUM_CONSTANTS, METHODS); 104 } 105 106 final TypeElement te; 107 final TypeElement parent; 108 109 final BaseConfiguration config; 110 final BaseOptions options; 111 final Utils utils; 112 final VisibleMemberCache mcache; 113 114 private List<VisibleMemberTable> allSuperclasses; 115 private List<VisibleMemberTable> allSuperinterfaces; 116 private List<VisibleMemberTable> parents; 117 118 119 private Map<Kind, List<Element>> extraMembers = new EnumMap<>(Kind.class); 120 private Map<Kind, List<Element>> visibleMembers = null; 121 private Map<ExecutableElement, PropertyMembers> propertyMap = new HashMap<>(); 122 123 // Keeps track of method overrides 124 Map<ExecutableElement, OverridingMethodInfo> overriddenMethodTable 125 = new LinkedHashMap<>(); 126 127 protected VisibleMemberTable(TypeElement typeElement, BaseConfiguration configuration, 128 VisibleMemberCache mcache) { 129 config = configuration; 130 utils = configuration.utils; 131 options = configuration.getOptions(); 132 te = typeElement; 133 parent = utils.getSuperClass(te); 134 this.mcache = mcache; 135 allSuperclasses = new ArrayList<>(); 136 allSuperinterfaces = new ArrayList<>(); 137 parents = new ArrayList<>(); 138 } 139 140 private synchronized void ensureInitialized() { 141 if (visibleMembers != null) 142 return; 143 144 visibleMembers = new EnumMap<>(Kind.class); 145 for (Kind kind : Kind.values()) { 146 visibleMembers.put(kind, new ArrayList<>()); 147 } 148 computeParents(); 149 computeVisibleMembers(); 150 } 151 152 List<? extends Element> getExtraMembers(Kind kind) { 153 ensureInitialized(); 154 return visibleMembers.getOrDefault(kind, Collections.emptyList()); 155 } 156 157 List<VisibleMemberTable> getAllSuperclasses() { 158 ensureInitialized(); 159 return allSuperclasses; 160 } 161 162 List<VisibleMemberTable> getAllSuperinterfaces() { 163 ensureInitialized(); 164 return allSuperinterfaces; 165 } 166 167 /** 168 * Returns a list of all visible enclosed members of a type element, 169 * and inherited members. 170 * <p> 171 * Notes: 172 * a. The list may or may not contain simple overridden methods. 173 * A simple overridden method is one that overrides a super method 174 * with no specification changes as indicated by the existence of a 175 * sole @inheritDoc or devoid of any API comments. 176 * <p> 177 * b.The list may contain (extra) members, inherited by inaccessible 178 * super types, primarily package private types. These members are 179 * required to be documented in the subtype when the super type is 180 * not documented. 181 * 182 * @param kind the member kind 183 * @return a list of all visible members 184 */ 185 public List<? extends Element> getAllVisibleMembers(Kind kind) { 186 ensureInitialized(); 187 return visibleMembers.getOrDefault(kind, Collections.emptyList()); 188 } 189 190 /** 191 * Returns a list of visible enclosed members of a specified kind, 192 * filtered by the specified predicate. 193 * @param kind the member kind 194 * @param p the predicate used to filter the output 195 * @return a list of visible enclosed members 196 */ 197 public List<? extends Element> getVisibleMembers(Kind kind, Predicate<Element> p) { 198 ensureInitialized(); 199 200 return visibleMembers.getOrDefault(kind, Collections.emptyList()).stream() 201 .filter(p) 202 .collect(Collectors.toList()); 203 } 204 205 /** 206 * Returns a list of all enclosed members including any extra members. 207 * Typically called by various builders. 208 * 209 * @param kind the member kind 210 * @return a list of visible enclosed members 211 */ 212 public List<? extends Element> getVisibleMembers(Kind kind) { 213 Predicate<Element> declaredAndLeafMembers = e -> { 214 TypeElement encl = utils.getEnclosingTypeElement(e); 215 return encl == te || utils.isUndocumentedEnclosure(encl); 216 }; 217 return getVisibleMembers(kind, declaredAndLeafMembers); 218 } 219 220 /** 221 * Returns a list of visible enclosed members of given kind, 222 * declared in this type element, and does not include 223 * any inherited members or extra members. 224 * 225 * @return a list of visible enclosed members in this type 226 */ 227 public List<? extends Element> getMembers(Kind kind) { 228 Predicate<Element> onlyLocallyDeclaredMembers = e -> utils.getEnclosingTypeElement(e) == te; 229 return getVisibleMembers(kind, onlyLocallyDeclaredMembers); 230 } 231 232 /** 233 * Returns the overridden method, if it is simply overriding or the 234 * method is a member of a package private type, this method is 235 * primarily used to determine the location of a possible comment. 236 * 237 * @param e the method to check 238 * @return the method found or null 239 */ 240 public ExecutableElement getOverriddenMethod(ExecutableElement e) { 241 ensureInitialized(); 242 243 OverridingMethodInfo found = overriddenMethodTable.get(e); 244 if (found != null 245 && (found.simpleOverride || utils.isUndocumentedEnclosure(utils.getEnclosingTypeElement(e)))) { 246 return found.overrider; 247 } 248 return null; 249 } 250 251 /** 252 * Returns the simply overridden method. 253 * @param e the method to check 254 * @return the overridden method or null 255 */ 256 public ExecutableElement getSimplyOverriddenMethod(ExecutableElement e) { 257 ensureInitialized(); 258 259 OverridingMethodInfo found = overriddenMethodTable.get(e); 260 if (found != null && found.simpleOverride) { 261 return found.overrider; 262 } 263 return null; 264 } 265 266 /** 267 * Returns a set of visible type elements in this type element's lineage. 268 * <p> 269 * This method returns the super-types in the inheritance 270 * order C, B, A, j.l.O. The super-interfaces however are 271 * alpha sorted and appended to the resulting set. 272 * 273 * @return the list of visible classes in this map. 274 */ 275 public Set<TypeElement> getVisibleTypeElements() { 276 ensureInitialized(); 277 Set<TypeElement> result = new LinkedHashSet<>(); 278 279 // Add this type element first. 280 result.add(te); 281 282 // Add the super classes. 283 allSuperclasses.stream() 284 .map(vmt -> vmt.te) 285 .forEach(result::add); 286 287 // ... and finally the sorted super interfaces. 288 allSuperinterfaces.stream() 289 .map(vmt -> vmt.te) 290 .sorted(utils.makeGeneralPurposeComparator()) 291 .forEach(result::add); 292 293 return result; 294 } 295 296 /** 297 * Returns true if this table contains visible members. 298 * 299 * @return true if visible members are present. 300 */ 301 public boolean hasVisibleMembers() { 302 for (Kind kind : Kind.values()) { 303 if (hasVisibleMembers(kind)) 304 return true; 305 } 306 return false; 307 } 308 309 /** 310 * Returns true if this table contains visible members of 311 * the specified kind, including inherited members. 312 * 313 * @return true if visible members are present. 314 */ 315 public boolean hasVisibleMembers(Kind kind) { 316 ensureInitialized(); 317 List<Element> elements = visibleMembers.get(kind); 318 return elements != null && !elements.isEmpty(); 319 } 320 321 /** 322 * Returns the property field associated with the property method. 323 * @param propertyMethod the identifying property method 324 * @return the field or null if absent 325 */ 326 public VariableElement getPropertyField(ExecutableElement propertyMethod) { 327 ensureInitialized(); 328 PropertyMembers pm = propertyMap.get(propertyMethod); 329 return pm == null ? null : pm.field; 330 } 331 332 /** 333 * Returns the getter method associated with the property method. 334 * @param propertyMethod the identifying property method 335 * @return the getter or null if absent 336 */ 337 public ExecutableElement getPropertyGetter(ExecutableElement propertyMethod) { 338 ensureInitialized(); 339 PropertyMembers pm = propertyMap.get(propertyMethod); 340 return pm == null ? null : pm.getter; 341 } 342 343 /** 344 * Returns the setter method associated with the property method. 345 * @param propertyMethod the identifying property method 346 * @return the setter or null if absent 347 */ 348 public ExecutableElement getPropertySetter(ExecutableElement propertyMethod) { 349 ensureInitialized(); 350 PropertyMembers pm = propertyMap.get(propertyMethod); 351 return pm == null ? null : pm.setter; 352 } 353 354 private void computeParents() { 355 for (TypeMirror intfType : te.getInterfaces()) { 356 TypeElement intfc = utils.asTypeElement(intfType); 357 if (intfc != null) { 358 VisibleMemberTable vmt = mcache.getVisibleMemberTable(intfc); 359 allSuperinterfaces.add(vmt); 360 parents.add(vmt); 361 allSuperinterfaces.addAll(vmt.getAllSuperinterfaces()); 362 } 363 } 364 365 if (parent != null) { 366 VisibleMemberTable vmt = mcache.getVisibleMemberTable(parent); 367 allSuperclasses.add(vmt); 368 allSuperclasses.addAll(vmt.getAllSuperclasses()); 369 // Add direct super interfaces of a super class, if any. 370 allSuperinterfaces.addAll(vmt.getAllSuperinterfaces()); 371 parents.add(vmt); 372 } 373 } 374 375 private void computeVisibleMembers() { 376 377 // Note: these have some baggage, and are redundant, 378 // allow this to be GC'ed. 379 LocalMemberTable lmt = new LocalMemberTable(); 380 381 for (Kind k : Kind.values()) { 382 computeLeafMembers(lmt, k); 383 computeVisibleMembers(lmt, k); 384 } 385 // All members have been computed, compute properties. 386 computeVisibleProperties(lmt); 387 } 388 389 private void computeLeafMembers(LocalMemberTable lmt, Kind kind) { 390 List<Element> list = new ArrayList<>(); 391 if (utils.isUndocumentedEnclosure(te)) { 392 list.addAll(lmt.getOrderedMembers(kind)); 393 } 394 parents.forEach(pvmt -> { 395 list.addAll(pvmt.getExtraMembers(kind)); 396 }); 397 extraMembers.put(kind, Collections.unmodifiableList(list)); 398 } 399 400 void computeVisibleMembers(LocalMemberTable lmt, Kind kind) { 401 switch (kind) { 402 case FIELDS: case INNER_CLASSES: 403 computeVisibleFieldsAndInnerClasses(lmt, kind); 404 return; 405 406 case METHODS: 407 computeVisibleMethods(lmt); 408 return; 409 410 // Defer properties related computations for later. 411 case PROPERTIES: 412 return; 413 414 default: 415 List<Element> list = lmt.getOrderedMembers(kind).stream() 416 .filter(this::mustDocument) 417 .collect(Collectors.toList()); 418 visibleMembers.put(kind, Collections.unmodifiableList(list)); 419 break; 420 } 421 } 422 423 private boolean mustDocument(Element e) { 424 return !utils.hasHiddenTag(e) && utils.shouldDocument(e); 425 } 426 427 private boolean allowInheritedMembers(Element e, Kind kind, LocalMemberTable lmt) { 428 return isInherited(e) && !isMemberHidden(e, kind, lmt); 429 } 430 431 private boolean isInherited(Element e) { 432 if (utils.isPrivate(e)) 433 return false; 434 435 if (utils.isPackagePrivate(e)) 436 // Allowed iff this type-element is in the same package as the element 437 return utils.containingPackage(e).equals(utils.containingPackage(te)); 438 439 return true; 440 } 441 442 private boolean isMemberHidden(Element inheritedMember, Kind kind, LocalMemberTable lmt) { 443 Elements elementUtils = config.docEnv.getElementUtils(); 444 switch(kind) { 445 default: 446 List<Element> list = lmt.getMembers(inheritedMember, kind); 447 if (list.isEmpty()) 448 return false; 449 return elementUtils.hides(list.get(0), inheritedMember); 450 case METHODS: case CONSTRUCTORS: // Handled elsewhere. 451 throw new IllegalArgumentException("incorrect kind"); 452 } 453 } 454 455 private void computeVisibleFieldsAndInnerClasses(LocalMemberTable lmt, Kind kind) { 456 Set<Element> result = new LinkedHashSet<>(); 457 for (VisibleMemberTable pvmt : parents) { 458 result.addAll(pvmt.getExtraMembers(kind)); 459 result.addAll(pvmt.getAllVisibleMembers(kind)); 460 } 461 462 // Filter out members in the inherited list that are hidden 463 // by this type or should not be inherited at all. 464 List<Element> list = result.stream() 465 .filter(e -> allowInheritedMembers(e, kind, lmt)).collect(Collectors.toList()); 466 467 // Prefix local results first 468 list.addAll(0, lmt.getOrderedMembers(kind)); 469 470 // Filter out elements that should not be documented 471 list = list.stream() 472 .filter(this::mustDocument) 473 .collect(Collectors.toList()); 474 475 visibleMembers.put(kind, Collections.unmodifiableList(list)); 476 } 477 478 private void computeVisibleMethods(LocalMemberTable lmt) { 479 Set<Element> inheritedMethods = new LinkedHashSet<>(); 480 Map<ExecutableElement, List<ExecutableElement>> overriddenByTable = new HashMap<>(); 481 for (VisibleMemberTable pvmt : parents) { 482 // Merge the lineage overrides into local table 483 pvmt.overriddenMethodTable.entrySet().forEach(e -> { 484 OverridingMethodInfo p = e.getValue(); 485 if (!p.simpleOverride) { // consider only real overrides 486 List<ExecutableElement> list = overriddenByTable.computeIfAbsent(p.overrider, 487 k -> new ArrayList<>()); 488 list.add(e.getKey()); 489 } 490 }); 491 inheritedMethods.addAll(pvmt.getAllVisibleMembers(Kind.METHODS)); 492 493 // Copy the extra members (if any) from the lineage. 494 if (!utils.shouldDocument(pvmt.te)) { 495 List<? extends Element> extraMethods = pvmt.getExtraMembers(Kind.METHODS); 496 497 if (lmt.getOrderedMembers(Kind.METHODS).isEmpty()) { 498 inheritedMethods.addAll(extraMethods); 499 continue; 500 } 501 502 // Check if an extra-method ought to percolate through. 503 for (Element extraMethod : extraMethods) { 504 boolean found = false; 505 506 List<Element> lmethods = lmt.getMembers(extraMethod, Kind.METHODS); 507 for (Element lmethod : lmethods) { 508 ExecutableElement method = (ExecutableElement)lmethod; 509 found = utils.elementUtils.overrides(method, 510 (ExecutableElement)extraMethod, te); 511 if (found) 512 break; 513 } 514 if (!found) 515 inheritedMethods.add(extraMethod); 516 } 517 } 518 } 519 520 // Filter out inherited methods that: 521 // a. cannot override (private instance members) 522 // b. are overridden and should not be visible in this type 523 // c. are hidden in the type being considered 524 // see allowInheritedMethods, which performs the above actions 525 List<Element> list = inheritedMethods.stream() 526 .filter(e -> allowInheritedMethods((ExecutableElement)e, overriddenByTable, lmt)) 527 .collect(Collectors.toList()); 528 529 // Filter out the local methods, that do not override or simply 530 // overrides a super method, or those methods that should not 531 // be visible. 532 Predicate<ExecutableElement> isVisible = m -> { 533 OverridingMethodInfo p = overriddenMethodTable.getOrDefault(m, null); 534 return p == null || !p.simpleOverride; 535 }; 536 List<Element> mlist = lmt.getOrderedMembers(Kind.METHODS); 537 List<Element> llist = mlist.stream() 538 .map(m -> (ExecutableElement)m) 539 .filter(isVisible) 540 .collect(Collectors.toList()); 541 542 // Merge the above lists, making sure the local methods precede 543 // the others 544 list.addAll(0, llist); 545 546 // Final filtration of elements 547 list = list.stream() 548 .filter(this::mustDocument) 549 .collect(Collectors.toList()); 550 551 visibleMembers.put(Kind.METHODS, Collections.unmodifiableList(list)); 552 553 // Copy over overridden tables from the lineage, and finish up. 554 for (VisibleMemberTable pvmt : parents) { 555 overriddenMethodTable.putAll(pvmt.overriddenMethodTable); 556 } 557 overriddenMethodTable = Collections.unmodifiableMap(overriddenMethodTable); 558 } 559 560 boolean isEnclosureInterface(Element e) { 561 TypeElement enclosing = utils.getEnclosingTypeElement(e); 562 return utils.isInterface(enclosing); 563 } 564 565 boolean allowInheritedMethods(ExecutableElement inheritedMethod, 566 Map<ExecutableElement, List<ExecutableElement>> inheritedOverriddenTable, 567 LocalMemberTable lmt) { 568 if (!isInherited(inheritedMethod)) 569 return false; 570 571 final boolean haveStatic = utils.isStatic(inheritedMethod); 572 final boolean inInterface = isEnclosureInterface(inheritedMethod); 573 574 // Static methods in interfaces are never documented. 575 if (haveStatic && inInterface) { 576 return false; 577 } 578 579 // Multiple-Inheritance: remove the interface method that may have 580 // been overridden by another interface method in the hierarchy 581 // 582 // Note: The following approach is very simplistic and is compatible 583 // with old VMM. A future enhancement, may include a contention breaker, 584 // to correctly eliminate those methods that are merely definitions 585 // in favor of concrete overriding methods, for instance those that have 586 // API documentation and are not abstract OR default methods. 587 if (inInterface) { 588 List<ExecutableElement> list = inheritedOverriddenTable.get(inheritedMethod); 589 if (list != null) { 590 boolean found = list.stream() 591 .anyMatch(this::isEnclosureInterface); 592 if (found) 593 return false; 594 } 595 } 596 597 Elements elementUtils = config.docEnv.getElementUtils(); 598 599 // Check the local methods in this type. 600 List<Element> lMethods = lmt.getMembers(inheritedMethod, Kind.METHODS); 601 for (Element le : lMethods) { 602 ExecutableElement lMethod = (ExecutableElement) le; 603 // Ignore private methods or those methods marked with 604 // a "hidden" tag. 605 if (utils.isPrivate(lMethod)) 606 continue; 607 608 // Remove methods that are "hidden", in JLS terms. 609 if (haveStatic && utils.isStatic(lMethod) && 610 elementUtils.hides(lMethod, inheritedMethod)) { 611 return false; 612 } 613 614 // Check for overriding methods. 615 if (elementUtils.overrides(lMethod, inheritedMethod, 616 utils.getEnclosingTypeElement(lMethod))) { 617 618 // Disallow package-private super methods to leak in 619 TypeElement encl = utils.getEnclosingTypeElement(inheritedMethod); 620 if (utils.isUndocumentedEnclosure(encl)) { 621 overriddenMethodTable.computeIfAbsent(lMethod, 622 l -> new OverridingMethodInfo(inheritedMethod, false)); 623 return false; 624 } 625 626 TypeMirror inheritedMethodReturn = inheritedMethod.getReturnType(); 627 TypeMirror lMethodReturn = lMethod.getReturnType(); 628 boolean covariantReturn = 629 lMethodReturn.getKind() == TypeKind.DECLARED 630 && inheritedMethodReturn.getKind() == TypeKind.DECLARED 631 && !utils.typeUtils.isSameType(lMethodReturn, inheritedMethodReturn) 632 && utils.typeUtils.isSubtype(lMethodReturn, inheritedMethodReturn); 633 boolean simpleOverride = covariantReturn ? false : utils.isSimpleOverride(lMethod); 634 overriddenMethodTable.computeIfAbsent(lMethod, 635 l -> new OverridingMethodInfo(inheritedMethod, simpleOverride)); 636 return simpleOverride; 637 } 638 } 639 return true; 640 } 641 642 /* 643 * This class encapsulates the details of local members, orderedMembers 644 * contains the members in the declaration order, additionally a 645 * HashMap is maintained for performance optimization to lookup 646 * members. As a future enhancement is perhaps to consolidate the ordering 647 * into a Map, capturing the insertion order, thereby eliminating an 648 * ordered list. 649 */ 650 class LocalMemberTable { 651 652 // Maintains declaration order 653 private final Map<Kind, List<Element>> orderedMembers; 654 655 // Performance optimization 656 private final Map<Kind, Map<String, List<Element>>> memberMap; 657 658 LocalMemberTable() { 659 orderedMembers = new EnumMap<>(Kind.class); 660 memberMap = new EnumMap<>(Kind.class); 661 662 List<? extends Element> elements = te.getEnclosedElements(); 663 for (Element e : elements) { 664 if (options.noDeprecated && utils.isDeprecated(e)) { 665 continue; 666 } 667 switch (e.getKind()) { 668 case CLASS: 669 case INTERFACE: 670 case ENUM: 671 case ANNOTATION_TYPE: 672 case RECORD: 673 addMember(e, Kind.INNER_CLASSES); 674 break; 675 case FIELD: 676 addMember(e, Kind.FIELDS); 677 addMember(e, Kind.ANNOTATION_TYPE_FIELDS); 678 break; 679 case METHOD: 680 ExecutableElement ee = (ExecutableElement)e; 681 if (utils.isAnnotationType(te)) { 682 addMember(e, ee.getDefaultValue() == null 683 ? Kind.ANNOTATION_TYPE_MEMBER_REQUIRED 684 : Kind.ANNOTATION_TYPE_MEMBER_OPTIONAL); 685 } 686 addMember(e, Kind.METHODS); 687 break; 688 case CONSTRUCTOR: 689 addMember(e, Kind.CONSTRUCTORS); 690 break; 691 case ENUM_CONSTANT: 692 addMember(e, Kind.ENUM_CONSTANTS); 693 break; 694 } 695 } 696 697 // Freeze the data structures 698 for (Kind kind : Kind.values()) { 699 orderedMembers.computeIfPresent(kind, (k, v) -> Collections.unmodifiableList(v)); 700 orderedMembers.computeIfAbsent(kind, t -> Collections.emptyList()); 701 702 memberMap.computeIfPresent(kind, (k, v) -> Collections.unmodifiableMap(v)); 703 memberMap.computeIfAbsent(kind, t -> Collections.emptyMap()); 704 } 705 } 706 707 @SuppressWarnings("preview") 708 String getMemberKey(Element e) { 709 return new SimpleElementVisitor14<String, Void>() { 710 @Override 711 public String visitExecutable(ExecutableElement e, Void aVoid) { 712 return e.getSimpleName() + ":" + e.getParameters().size(); 713 } 714 715 @Override 716 protected String defaultAction(Element e, Void aVoid) { 717 return e.getSimpleName().toString(); 718 } 719 }.visit(e); 720 } 721 722 void addMember(Element e, Kind kind) { 723 List<Element> list = orderedMembers.computeIfAbsent(kind, k -> new ArrayList<>()); 724 list.add(e); 725 726 Map<String, List<Element>> map = memberMap.computeIfAbsent(kind, k -> new HashMap<>()); 727 list = map.computeIfAbsent(getMemberKey(e), l -> new ArrayList<>()); 728 list.add(e); 729 } 730 731 List<Element> getOrderedMembers(Kind kind) { 732 return orderedMembers.get(kind); 733 } 734 735 List<Element> getMembers(Element e, Kind kind) { 736 String key = getMemberKey(e); 737 return getMembers(key, kind); 738 } 739 740 List<Element> getMembers(String key, Kind kind) { 741 Map <String, List<Element>> map = memberMap.get(kind); 742 return map.getOrDefault(key, Collections.emptyList()); 743 } 744 745 List<Element> getPropertyMethods(String methodName, int argcount) { 746 return getMembers(methodName + ":" + argcount, Kind.METHODS).stream() 747 .filter(m -> (utils.isPublic(m) || utils.isProtected(m))) 748 .collect(Collectors.toList()); 749 } 750 } 751 752 /** 753 * The properties triad for a property method. 754 */ 755 static class PropertyMembers { 756 final VariableElement field; 757 final ExecutableElement getter; 758 final ExecutableElement setter; 759 760 PropertyMembers(VariableElement field, ExecutableElement getter, ExecutableElement setter) { 761 this.field = field; 762 this.getter = getter; 763 this.setter = setter; 764 } 765 766 public String toString() { 767 return ("field: " + field + ", getter: " + getter + ", setter: " + setter); 768 } 769 } 770 771 /* 772 * JavaFX convention notes. 773 * A JavaFX property-method is a method, which ends with "Property" in 774 * its name, takes no parameters and typically returns a subtype of javafx.beans. 775 * ReadOnlyProperty, in the strictest sense. However, it may not always 776 * be possible for the doclet to have access to j.b.ReadOnlyProperty, 777 * for this reason the strict check is disabled via an undocumented flag. 778 * 779 * Note, a method should not be considered as a property-method, 780 * if it satisfied the previously stated conditions AND if the 781 * method begins with "set", "get" or "is". 782 * 783 * Supposing we have {@code BooleanProperty acmeProperty()}, then the 784 * property-name is "acme". 785 * 786 * Property field, one may or may not exist and could be private, and 787 * should match the property-method. 788 * 789 * A property-setter is a method starting with "set", and the 790 * first character of the upper-cased starting character of the property name, the 791 * method must take 1 argument and must return a <code>void</code>. 792 * 793 * Using the above example {@code void setAcme(Something s)} can be 794 * considered as a property-setter of the property "acme". 795 * 796 * A property-getter is a method starting with "get" and the first character 797 * upper-cased property-name, having no parameters. A method that does not take any 798 * parameters and starting with "is" and an upper-cased property-name, 799 * returning a primitive type boolean or BooleanProperty can also be 800 * considered as a getter, however there must be only one getter for every property. 801 * 802 * For example {@code Object getAcme()} is a property-getter, and 803 * {@code boolean isFoo()} 804 */ 805 private void computeVisibleProperties(LocalMemberTable lmt) { 806 if (!options.javafx) 807 return; 808 809 PropertyUtils pUtils = config.propertyUtils; 810 List<ExecutableElement> list = visibleMembers.getOrDefault(Kind.METHODS, Collections.emptyList()) 811 .stream() 812 .map(m -> (ExecutableElement)m) 813 .filter(pUtils::isPropertyMethod) 814 .collect(Collectors.toList()); 815 816 visibleMembers.put(Kind.PROPERTIES, Collections.unmodifiableList(list)); 817 818 List<ExecutableElement> propertyMethods = list.stream() 819 .filter(e -> utils.getEnclosingTypeElement(e) == te) 820 .collect(Collectors.toList()); 821 822 // Compute additional properties related sundries. 823 for (ExecutableElement propertyMethod : propertyMethods) { 824 String baseName = pUtils.getBaseName(propertyMethod); 825 List<Element> flist = lmt.getMembers(baseName, Kind.FIELDS); 826 Element field = flist.isEmpty() ? null : flist.get(0); 827 828 Element getter = null, setter = null; 829 List<Element> found = lmt.getPropertyMethods(pUtils.getGetName(propertyMethod), 0); 830 if (!found.isEmpty()) { 831 // Getters have zero params, no overloads! pick the first. 832 getter = found.get(0); 833 } 834 if (getter == null) { 835 // Check if isProperty methods are present ? 836 found = lmt.getPropertyMethods(pUtils.getIsName(propertyMethod), 0); 837 if (!found.isEmpty()) { 838 String propertyTypeName = propertyMethod.getReturnType().toString(); 839 // Check if the return type of property method matches an isProperty method. 840 if (pUtils.hasIsMethod(propertyMethod)) { 841 // Getters have zero params, no overloads!, pick the first. 842 getter = found.get(0); 843 } 844 } 845 } 846 found = lmt.getPropertyMethods(pUtils.getSetName(propertyMethod), 1); 847 if (found != null) { 848 for (Element e : found) { 849 if (pUtils.isValidSetterMethod((ExecutableElement)e)) { 850 setter = e; 851 break; 852 } 853 } 854 } 855 856 propertyMap.put(propertyMethod, new PropertyMembers((VariableElement)field, 857 (ExecutableElement)getter, (ExecutableElement)setter)); 858 859 // Debugging purposes 860 // System.out.println("te: " + te + ": " + utils.getEnclosingTypeElement(propertyMethod) + 861 // ":" + propertyMethod.toString() + "->" + propertyMap.get(propertyMethod)); 862 } 863 } 864 865 866 // Future cleanups 867 868 Map<ExecutableElement, SoftReference<ImplementedMethods>> implementMethodsFinders = new HashMap<>(); 869 870 private ImplementedMethods getImplementedMethodsFinder(ExecutableElement method) { 871 SoftReference<ImplementedMethods> imf = implementMethodsFinders.get(method); 872 // IMF does not exist or referent was gc'ed away ? 873 if (imf == null || imf.get() == null) { 874 imf = new SoftReference<>(new ImplementedMethods(method)); 875 implementMethodsFinders.put(method, imf); 876 } 877 return imf.get(); 878 } 879 880 public List<ExecutableElement> getImplementedMethods(ExecutableElement method) { 881 ImplementedMethods imf = getImplementedMethodsFinder(method); 882 return imf.getImplementedMethods().stream() 883 .filter(m -> getSimplyOverriddenMethod(m) == null) 884 .collect(Collectors.toList()); 885 } 886 887 public TypeMirror getImplementedMethodHolder(ExecutableElement method, 888 ExecutableElement implementedMethod) { 889 ImplementedMethods imf = getImplementedMethodsFinder(method); 890 return imf.getMethodHolder(implementedMethod); 891 } 892 893 private class ImplementedMethods { 894 895 private final Map<ExecutableElement, TypeMirror> interfaces = new HashMap<>(); 896 private final List<ExecutableElement> methlist = new ArrayList<>(); 897 private final TypeElement typeElement; 898 private final ExecutableElement method; 899 900 public ImplementedMethods(ExecutableElement method) { 901 this.method = method; 902 typeElement = utils.getEnclosingTypeElement(method); 903 Set<TypeMirror> intfacs = utils.getAllInterfaces(typeElement); 904 /* 905 * Search for the method in the list of interfaces. If found check if it is 906 * overridden by any other subinterface method which this class 907 * implements. If it is not overridden, add it in the method list. 908 * Do this recursively for all the extended interfaces for each interface 909 * from the list. 910 */ 911 for (TypeMirror interfaceType : intfacs) { 912 ExecutableElement found = utils.findMethod(utils.asTypeElement(interfaceType), method); 913 if (found != null) { 914 removeOverriddenMethod(found); 915 if (!overridingMethodFound(found)) { 916 methlist.add(found); 917 interfaces.put(found, interfaceType); 918 } 919 } 920 } 921 } 922 923 /** 924 * Return the list of interface methods which the method passed in the 925 * constructor is implementing. The search/build order is as follows: 926 * <pre> 927 * 1. Search in all the immediate interfaces which this method's class is 928 * implementing. Do it recursively for the superinterfaces as well. 929 * 2. Traverse all the superclasses and search recursively in the 930 * interfaces which those superclasses implement. 931 *</pre> 932 * 933 * @return SortedSet<ExecutableElement> of implemented methods. 934 */ 935 List<ExecutableElement> getImplementedMethods() { 936 return methlist; 937 } 938 939 TypeMirror getMethodHolder(ExecutableElement ee) { 940 return interfaces.get(ee); 941 } 942 943 /** 944 * Search in the method list and check if it contains a method which 945 * is overridden by the method as parameter. If found, remove the 946 * overridden method from the method list. 947 * 948 * @param method Is this method overriding a method in the method list. 949 */ 950 private void removeOverriddenMethod(ExecutableElement method) { 951 TypeElement overriddenClass = utils.overriddenClass(method); 952 if (overriddenClass != null) { 953 for (int i = 0; i < methlist.size(); i++) { 954 TypeElement te = utils.getEnclosingTypeElement(methlist.get(i)); 955 if (te == overriddenClass || utils.isSubclassOf(overriddenClass, te)) { 956 methlist.remove(i); // remove overridden method 957 return; 958 } 959 } 960 } 961 } 962 963 /** 964 * Search in the already found methods' list and check if it contains 965 * a method which is overriding the method parameter or is the method 966 * parameter itself. 967 * 968 * @param method method to be searched 969 */ 970 private boolean overridingMethodFound(ExecutableElement method) { 971 TypeElement containingClass = utils.getEnclosingTypeElement(method); 972 for (ExecutableElement listmethod : methlist) { 973 if (containingClass == utils.getEnclosingTypeElement(listmethod)) { 974 // it's the same method. 975 return true; 976 } 977 TypeElement te = utils.overriddenClass(listmethod); 978 if (te == null) { 979 continue; 980 } 981 if (te == containingClass || utils.isSubclassOf(te, containingClass)) { 982 return true; 983 } 984 } 985 return false; 986 } 987 } 988 989 /** 990 * A simple container to encapsulate an overriding method 991 * and the type of override. 992 */ 993 static class OverridingMethodInfo { 994 final ExecutableElement overrider; 995 final boolean simpleOverride; 996 997 public OverridingMethodInfo(ExecutableElement overrider, boolean simpleOverride) { 998 this.overrider = overrider; 999 this.simpleOverride = simpleOverride; 1000 } 1001 1002 @Override 1003 public String toString() { 1004 return "OverridingMethodInfo[" + overrider + ",simple:" + simpleOverride + "]"; 1005 } 1006 } 1007 }