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