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 commments. 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 overridding 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 inhertied 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 addMember(e, Kind.INNER_CLASSES); 670 break; 671 case FIELD: 672 addMember(e, Kind.FIELDS); 673 addMember(e, Kind.ANNOTATION_TYPE_FIELDS); 674 break; 675 case METHOD: 676 ExecutableElement ee = (ExecutableElement)e; 677 if (utils.isAnnotationType(te)) { 678 addMember(e, ee.getDefaultValue() == null 679 ? Kind.ANNOTATION_TYPE_MEMBER_REQUIRED 680 : Kind.ANNOTATION_TYPE_MEMBER_OPTIONAL); 681 } 682 addMember(e, Kind.METHODS); 683 break; 684 case CONSTRUCTOR: 685 addMember(e, Kind.CONSTRUCTORS); 686 break; 687 case ENUM_CONSTANT: 688 addMember(e, Kind.ENUM_CONSTANTS); 689 break; 690 } 691 } 692 693 // Freeze the data structures 694 for (Kind kind : Kind.values()) { 695 orderedMembers.computeIfPresent(kind, (k, v) -> Collections.unmodifiableList(v)); 696 orderedMembers.computeIfAbsent(kind, t -> Collections.emptyList()); 697 698 memberMap.computeIfPresent(kind, (k, v) -> Collections.unmodifiableMap(v)); 699 memberMap.computeIfAbsent(kind, t -> Collections.emptyMap()); 700 } 701 } 702 703 @SuppressWarnings("preview") 704 String getMemberKey(Element e) { 705 return new SimpleElementVisitor14<String, Void>() { 706 @Override 707 public String visitExecutable(ExecutableElement e, Void aVoid) { 708 return e.getSimpleName() + ":" + e.getParameters().size(); 709 } 710 711 @Override 712 protected String defaultAction(Element e, Void aVoid) { 713 return e.getSimpleName().toString(); 714 } 715 }.visit(e); 716 } 717 718 void addMember(Element e, Kind kind) { 719 List<Element> list = orderedMembers.computeIfAbsent(kind, k -> new ArrayList<>()); 720 list.add(e); 721 722 Map<String, List<Element>> map = memberMap.computeIfAbsent(kind, k -> new HashMap<>()); 723 list = map.computeIfAbsent(getMemberKey(e), l -> new ArrayList<>()); 724 list.add(e); 725 } 726 727 List<Element> getOrderedMembers(Kind kind) { 728 return orderedMembers.get(kind); 729 } 730 731 List<Element> getMembers(Element e, Kind kind) { 732 String key = getMemberKey(e); 733 return getMembers(key, kind); 734 } 735 736 List<Element> getMembers(String key, Kind kind) { 737 Map <String, List<Element>> map = memberMap.get(kind); 738 return map.getOrDefault(key, Collections.emptyList()); 739 } 740 741 List<Element> getPropertyMethods(String methodName, int argcount) { 742 return getMembers(methodName + ":" + argcount, Kind.METHODS).stream() 743 .filter(m -> (utils.isPublic(m) || utils.isProtected(m))) 744 .collect(Collectors.toList()); 745 } 746 } 747 748 /** 749 * The properties triad for a property method. 750 */ 751 static class PropertyMembers { 752 final VariableElement field; 753 final ExecutableElement getter; 754 final ExecutableElement setter; 755 756 PropertyMembers(VariableElement field, ExecutableElement getter, ExecutableElement setter) { 757 this.field = field; 758 this.getter = getter; 759 this.setter = setter; 760 } 761 762 public String toString() { 763 return ("field: " + field + ", getter: " + getter + ", setter: " + setter); 764 } 765 } 766 767 /* 768 * JavaFX convention notes. 769 * A JavaFX property-method is a method, which ends with "Property" in 770 * its name, takes no parameters and typically returns a subtype of javafx.beans. 771 * ReadOnlyProperty, in the strictest sense. However, it may not always 772 * be possible for the doclet to have access to j.b.ReadOnlyProperty, 773 * for this reason the strict check is disabled via an undocumented flag. 774 * 775 * Note, a method should not be considered as a property-method, 776 * if it satisfied the previously stated conditions AND if the 777 * method begins with "set", "get" or "is". 778 * 779 * Supposing we have {@code BooleanProperty acmeProperty()}, then the 780 * property-name is "acme". 781 * 782 * Property field, one may or may not exist and could be private, and 783 * should match the property-method. 784 * 785 * A property-setter is a method starting with "set", and the 786 * first character of the upper-cased starting character of the property name, the 787 * method must take 1 argument and must return a <code>void</code>. 788 * 789 * Using the above example {@code void setAcme(Something s)} can be 790 * considered as a property-setter of the property "acme". 791 * 792 * A property-getter is a method starting with "get" and the first character 793 * upper-cased property-name, having no parameters. A method that does not take any 794 * parameters and starting with "is" and an upper-cased property-name, 795 * returning a primitive type boolean or BooleanProperty can also be 796 * considered as a getter, however there must be only one getter for every property. 797 * 798 * For example {@code Object getAcme()} is a property-getter, and 799 * {@code boolean isFoo()} 800 */ 801 private void computeVisibleProperties(LocalMemberTable lmt) { 802 if (!config.javafx) 803 return; 804 805 PropertyUtils pUtils = config.propertyUtils; 806 List<ExecutableElement> list = visibleMembers.getOrDefault(Kind.METHODS, Collections.emptyList()) 807 .stream() 808 .map(m -> (ExecutableElement)m) 809 .filter(pUtils::isPropertyMethod) 810 .collect(Collectors.toList()); 811 812 visibleMembers.put(Kind.PROPERTIES, Collections.unmodifiableList(list)); 813 814 List<ExecutableElement> propertyMethods = list.stream() 815 .filter(e -> utils.getEnclosingTypeElement(e) == te) 816 .collect(Collectors.toList()); 817 818 // Compute additional properties related sundries. 819 for (ExecutableElement propertyMethod : propertyMethods) { 820 String baseName = pUtils.getBaseName(propertyMethod); 821 List<Element> flist = lmt.getMembers(baseName, Kind.FIELDS); 822 Element field = flist.isEmpty() ? null : flist.get(0); 823 824 Element getter = null, setter = null; 825 List<Element> found = lmt.getPropertyMethods(pUtils.getGetName(propertyMethod), 0); 826 if (!found.isEmpty()) { 827 // Getters have zero params, no overloads! pick the first. 828 getter = found.get(0); 829 } 830 if (getter == null) { 831 // Check if isProperty methods are present ? 832 found = lmt.getPropertyMethods(pUtils.getIsName(propertyMethod), 0); 833 if (!found.isEmpty()) { 834 String propertyTypeName = propertyMethod.getReturnType().toString(); 835 // Check if the return type of property method matches an isProperty method. 836 if (pUtils.hasIsMethod(propertyMethod)) { 837 // Getters have zero params, no overloads!, pick the first. 838 getter = found.get(0); 839 } 840 } 841 } 842 found = lmt.getPropertyMethods(pUtils.getSetName(propertyMethod), 1); 843 if (found != null) { 844 for (Element e : found) { 845 if (pUtils.isValidSetterMethod((ExecutableElement)e)) { 846 setter = e; 847 break; 848 } 849 } 850 } 851 852 propertyMap.put(propertyMethod, new PropertyMembers((VariableElement)field, 853 (ExecutableElement)getter, (ExecutableElement)setter)); 854 855 // Debugging purposes 856 // System.out.println("te: " + te + ": " + utils.getEnclosingTypeElement(propertyMethod) + 857 // ":" + propertyMethod.toString() + "->" + propertyMap.get(propertyMethod)); 858 } 859 } 860 861 862 // Future cleanups 863 864 Map<ExecutableElement, SoftReference<ImplementedMethods>> implementMethodsFinders = new HashMap<>(); 865 866 private ImplementedMethods getImplementedMethodsFinder(ExecutableElement method) { 867 SoftReference<ImplementedMethods> imf = implementMethodsFinders.get(method); 868 // IMF does not exist or referent was gc'ed away ? 869 if (imf == null || imf.get() == null) { 870 imf = new SoftReference<>(new ImplementedMethods(method)); 871 implementMethodsFinders.put(method, imf); 872 } 873 return imf.get(); 874 } 875 876 public List<ExecutableElement> getImplementedMethods(ExecutableElement method) { 877 ImplementedMethods imf = getImplementedMethodsFinder(method); 878 return imf.getImplementedMethods().stream() 879 .filter(m -> getSimplyOverriddenMethod(m) == null) 880 .collect(Collectors.toList()); 881 } 882 883 public TypeMirror getImplementedMethodHolder(ExecutableElement method, 884 ExecutableElement implementedMethod) { 885 ImplementedMethods imf = getImplementedMethodsFinder(method); 886 return imf.getMethodHolder(implementedMethod); 887 } 888 889 private class ImplementedMethods { 890 891 private final Map<ExecutableElement, TypeMirror> interfaces = new HashMap<>(); 892 private final List<ExecutableElement> methlist = new ArrayList<>(); 893 private final TypeElement typeElement; 894 private final ExecutableElement method; 895 896 public ImplementedMethods(ExecutableElement method) { 897 this.method = method; 898 typeElement = utils.getEnclosingTypeElement(method); 899 Set<TypeMirror> intfacs = utils.getAllInterfaces(typeElement); 900 /* 901 * Search for the method in the list of interfaces. If found check if it is 902 * overridden by any other subinterface method which this class 903 * implements. If it is not overidden, add it in the method list. 904 * Do this recursively for all the extended interfaces for each interface 905 * from the list. 906 */ 907 for (TypeMirror interfaceType : intfacs) { 908 ExecutableElement found = utils.findMethod(utils.asTypeElement(interfaceType), method); 909 if (found != null) { 910 removeOverriddenMethod(found); 911 if (!overridingMethodFound(found)) { 912 methlist.add(found); 913 interfaces.put(found, interfaceType); 914 } 915 } 916 } 917 } 918 919 /** 920 * Return the list of interface methods which the method passed in the 921 * constructor is implementing. The search/build order is as follows: 922 * <pre> 923 * 1. Search in all the immediate interfaces which this method's class is 924 * implementing. Do it recursively for the superinterfaces as well. 925 * 2. Traverse all the superclasses and search recursively in the 926 * interfaces which those superclasses implement. 927 *</pre> 928 * 929 * @return SortedSet<ExecutableElement> of implemented methods. 930 */ 931 List<ExecutableElement> getImplementedMethods() { 932 return methlist; 933 } 934 935 TypeMirror getMethodHolder(ExecutableElement ee) { 936 return interfaces.get(ee); 937 } 938 939 /** 940 * Search in the method list and check if it contains a method which 941 * is overridden by the method as parameter. If found, remove the 942 * overridden method from the method list. 943 * 944 * @param method Is this method overriding a method in the method list. 945 */ 946 private void removeOverriddenMethod(ExecutableElement method) { 947 TypeElement overriddenClass = utils.overriddenClass(method); 948 if (overriddenClass != null) { 949 for (int i = 0; i < methlist.size(); i++) { 950 TypeElement te = utils.getEnclosingTypeElement(methlist.get(i)); 951 if (te == overriddenClass || utils.isSubclassOf(overriddenClass, te)) { 952 methlist.remove(i); // remove overridden method 953 return; 954 } 955 } 956 } 957 } 958 959 /** 960 * Search in the already found methods' list and check if it contains 961 * a method which is overriding the method parameter or is the method 962 * parameter itself. 963 * 964 * @param method method to be searched 965 */ 966 private boolean overridingMethodFound(ExecutableElement method) { 967 TypeElement containingClass = utils.getEnclosingTypeElement(method); 968 for (ExecutableElement listmethod : methlist) { 969 if (containingClass == utils.getEnclosingTypeElement(listmethod)) { 970 // it's the same method. 971 return true; 972 } 973 TypeElement te = utils.overriddenClass(listmethod); 974 if (te == null) { 975 continue; 976 } 977 if (te == containingClass || utils.isSubclassOf(te, containingClass)) { 978 return true; 979 } 980 } 981 return false; 982 } 983 } 984 985 /** 986 * A simple container to encapsulate an overriding method 987 * and the type of override. 988 */ 989 static class OverridingMethodInfo { 990 final ExecutableElement overrider; 991 final boolean simpleOverride; 992 993 public OverridingMethodInfo(ExecutableElement overrider, boolean simpleOverride) { 994 this.overrider = overrider; 995 this.simpleOverride = simpleOverride; 996 } 997 998 @Override 999 public String toString() { 1000 return "OverridingMethodInfo[" + overrider + ",simple:" + simpleOverride + "]"; 1001 } 1002 } 1003 }