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 &commat;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 }