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 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 }