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