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