1 /*
   2  * Copyright (c) 2003, 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.builders;
  27 
  28 import java.text.MessageFormat;
  29 import java.util.*;
  30 import java.util.stream.Collectors;
  31 
  32 import javax.lang.model.element.Element;
  33 import javax.lang.model.element.ExecutableElement;
  34 import javax.lang.model.element.TypeElement;
  35 import javax.lang.model.element.VariableElement;
  36 import javax.lang.model.util.ElementFilter;
  37 
  38 import com.sun.source.doctree.DocCommentTree;
  39 import com.sun.source.doctree.DocTree;
  40 import com.sun.source.doctree.DocTree.Kind;
  41 import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter;
  42 import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
  43 import jdk.javadoc.internal.doclets.toolkit.Content;
  44 import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter;
  45 import jdk.javadoc.internal.doclets.toolkit.WriterFactory;
  46 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
  47 import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
  48 import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable;
  49 import jdk.javadoc.internal.doclets.toolkit.CommentUtils;
  50 
  51 import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*;
  52 
  53 /**
  54  * Builds the member summary.
  55  * There are two anonymous subtype variants of this builder, created
  56  * in the {@link #getInstance} methods. One is for general types;
  57  * the other is for annotation types.
  58  *
  59  *  <p><b>This is NOT part of any supported API.
  60  *  If you write code that depends on this, you do so at your own risk.
  61  *  This code and its internal interfaces are subject to change or
  62  *  deletion without notice.</b>
  63  *
  64  * @author Jamie Ho
  65  * @author Bhavesh Patel (Modified)
  66  */
  67 public abstract class MemberSummaryBuilder extends AbstractMemberBuilder {
  68 
  69     /*
  70      * Comparator used to sort the members in the summary.
  71      */
  72     private final Comparator<Element> comparator;
  73 
  74     /**
  75      * The member summary writers for the given class.
  76      */
  77     private final EnumMap<VisibleMemberTable.Kind, MemberSummaryWriter> memberSummaryWriters;
  78 
  79     final PropertyHelper pHelper;
  80 
  81     /**
  82      * Construct a new MemberSummaryBuilder.
  83      *
  84      * @param context       the build context.
  85      * @param typeElement   the type element.
  86      */
  87     private MemberSummaryBuilder(Context context, TypeElement typeElement) {
  88         super(context, typeElement);
  89         memberSummaryWriters = new EnumMap<>(VisibleMemberTable.Kind.class);
  90         comparator = utils.makeIndexUseComparator();
  91         pHelper = new PropertyHelper(this);
  92     }
  93 
  94     /**
  95      * Construct a new MemberSummaryBuilder for a general type.
  96      *
  97      * @param classWriter   the writer for the class whose members are being
  98      *                      summarized.
  99      * @param context       the build context.
 100      * @return              the instance
 101      */
 102     public static MemberSummaryBuilder getInstance(
 103             ClassWriter classWriter, Context context) {
 104         MemberSummaryBuilder builder = new MemberSummaryBuilder(context, classWriter.getTypeElement()) {
 105             @Override
 106             public void build(Content contentTree) {
 107                 buildPropertiesSummary(contentTree);
 108                 buildNestedClassesSummary(contentTree);
 109                 buildEnumConstantsSummary(contentTree);
 110                 buildFieldsSummary(contentTree);
 111                 buildConstructorsSummary(contentTree);
 112                 buildMethodsSummary(contentTree);
 113             }
 114 
 115             @Override
 116             public boolean hasMembersToDocument() {
 117                 return visibleMemberTable.hasVisibleMembers();
 118             }
 119         };
 120         WriterFactory wf = context.configuration.getWriterFactory();
 121         for (VisibleMemberTable.Kind kind : VisibleMemberTable.Kind.values()) {
 122             MemberSummaryWriter msw = builder.getVisibleMemberTable().hasVisibleMembers(kind)
 123                     ? wf.getMemberSummaryWriter(classWriter, kind)
 124                     : null;
 125             builder.memberSummaryWriters.put(kind, msw);
 126         }
 127         return builder;
 128     }
 129 
 130     /**
 131      * Construct a new MemberSummaryBuilder for an annotation type.
 132      *
 133      * @param annotationTypeWriter the writer for the class whose members are
 134      *                             being summarized.
 135      * @param context       the build context.
 136      * @return              the instance
 137      */
 138     public static MemberSummaryBuilder getInstance(
 139             AnnotationTypeWriter annotationTypeWriter, Context context) {
 140         MemberSummaryBuilder builder = new MemberSummaryBuilder(context,
 141                 annotationTypeWriter.getAnnotationTypeElement()) {
 142             @Override
 143             public void build(Content contentTree) {
 144                 buildAnnotationTypeFieldsSummary(contentTree);
 145                 buildAnnotationTypeRequiredMemberSummary(contentTree);
 146                 buildAnnotationTypeOptionalMemberSummary(contentTree);
 147             }
 148 
 149             @Override
 150             public boolean hasMembersToDocument() {
 151                 return !utils.getAnnotationMembers(typeElement).isEmpty();
 152             }
 153         };
 154         WriterFactory wf = context.configuration.getWriterFactory();
 155         for (VisibleMemberTable.Kind kind : VisibleMemberTable.Kind.values()) {
 156             MemberSummaryWriter msw = builder.getVisibleMemberTable().hasVisibleMembers(kind)
 157                     ? wf.getMemberSummaryWriter(annotationTypeWriter, kind)
 158                     : null;
 159             builder.memberSummaryWriters.put(kind, msw);
 160         }
 161         return builder;
 162     }
 163 
 164     /**
 165      * Return the specified visible member map.
 166      *
 167      * @return the specified visible member map.
 168      * @throws ArrayIndexOutOfBoundsException when the type is invalid.
 169      * @see VisibleMemberTable
 170      */
 171     public VisibleMemberTable getVisibleMemberTable() {
 172         return visibleMemberTable;
 173     }
 174 
 175     /**.
 176      * Return the specified member summary writer.
 177      *
 178      * @param kind the kind of member summary writer to return.
 179      * @return the specified member summary writer.
 180      * @throws ArrayIndexOutOfBoundsException when the type is invalid.
 181      * @see VisibleMemberTable
 182      */
 183     public MemberSummaryWriter getMemberSummaryWriter(VisibleMemberTable.Kind kind) {
 184         return memberSummaryWriters.get(kind);
 185     }
 186 
 187     /**
 188      * Returns a list of methods that will be documented for the given class.
 189      * This information can be used for doclet specific documentation
 190      * generation.
 191      *
 192      * @param kind the kind of elements to return.
 193      * @return a list of methods that will be documented.
 194      * @see VisibleMemberTable
 195      */
 196     public SortedSet<Element> members(VisibleMemberTable.Kind kind) {
 197         TreeSet<Element> out = new TreeSet<>(comparator);
 198         out.addAll(getVisibleMembers(kind));
 199         return out;
 200     }
 201 
 202     /**
 203      * Returns true if there are members of the given kind, false otherwise.
 204      * @param kind
 205      * @return true if there are members of the given kind, false otherwise
 206      */
 207     public boolean hasMembers(VisibleMemberTable.Kind kind) {
 208         return !getVisibleMembers(kind).isEmpty();
 209     }
 210 
 211     /**
 212      * Build the summary for the enum constants.
 213      *
 214      * @param memberSummaryTree the content tree to which the documentation will be added
 215      */
 216     protected void buildEnumConstantsSummary(Content memberSummaryTree) {
 217         MemberSummaryWriter writer = memberSummaryWriters.get(ENUM_CONSTANTS);
 218         addSummary(writer, ENUM_CONSTANTS, false, memberSummaryTree);
 219     }
 220 
 221     /**
 222      * Build the summary for fields.
 223      *
 224      * @param memberSummaryTree the content tree to which the documentation will be added
 225      */
 226     protected void buildAnnotationTypeFieldsSummary(Content memberSummaryTree) {
 227         MemberSummaryWriter writer = memberSummaryWriters.get(ANNOTATION_TYPE_FIELDS);
 228         addSummary(writer, ANNOTATION_TYPE_FIELDS, false, memberSummaryTree);
 229     }
 230 
 231     /**
 232      * Build the summary for the optional members.
 233      *
 234      * @param memberSummaryTree the content tree to which the documentation will be added
 235      */
 236     protected void buildAnnotationTypeOptionalMemberSummary(Content memberSummaryTree) {
 237         MemberSummaryWriter writer = memberSummaryWriters.get(ANNOTATION_TYPE_MEMBER_OPTIONAL);
 238         addSummary(writer, ANNOTATION_TYPE_MEMBER_OPTIONAL, false, memberSummaryTree);
 239     }
 240 
 241     /**
 242      * Build the summary for the optional members.
 243      *
 244      * @param memberSummaryTree the content tree to which the documentation will be added
 245      */
 246     protected void buildAnnotationTypeRequiredMemberSummary(Content memberSummaryTree) {
 247         MemberSummaryWriter writer = memberSummaryWriters.get(ANNOTATION_TYPE_MEMBER_REQUIRED);
 248         addSummary(writer, ANNOTATION_TYPE_MEMBER_REQUIRED, false, memberSummaryTree);
 249     }
 250 
 251     /**
 252      * Build the summary for the fields.
 253      *
 254      * @param memberSummaryTree the content tree to which the documentation will be added
 255      */
 256     protected void buildFieldsSummary(Content memberSummaryTree) {
 257         MemberSummaryWriter writer = memberSummaryWriters.get(FIELDS);
 258         addSummary(writer, FIELDS, true, memberSummaryTree);
 259     }
 260 
 261     /**
 262      * Build the summary for the fields.
 263      *
 264      * @param memberSummaryTree the content tree to which the documentation will be added
 265      */
 266     protected void buildPropertiesSummary(Content memberSummaryTree) {
 267         MemberSummaryWriter writer = memberSummaryWriters.get(PROPERTIES);
 268         addSummary(writer, PROPERTIES, true, memberSummaryTree);
 269     }
 270 
 271     /**
 272      * Build the summary for the nested classes.
 273      *
 274      * @param memberSummaryTree the content tree to which the documentation will be added
 275      */
 276     protected void buildNestedClassesSummary(Content memberSummaryTree) {
 277         MemberSummaryWriter writer = memberSummaryWriters.get(INNER_CLASSES);
 278         addSummary(writer, INNER_CLASSES, true, memberSummaryTree);
 279     }
 280 
 281     /**
 282      * Build the method summary.
 283      *
 284      * @param memberSummaryTree the content tree to which the documentation will be added
 285      */
 286     protected void buildMethodsSummary(Content memberSummaryTree) {
 287         MemberSummaryWriter writer = memberSummaryWriters.get(METHODS);
 288         addSummary(writer, METHODS, true, memberSummaryTree);
 289     }
 290 
 291     /**
 292      * Build the constructor summary.
 293      *
 294      * @param memberSummaryTree the content tree to which the documentation will be added
 295      */
 296     protected void buildConstructorsSummary(Content memberSummaryTree) {
 297         MemberSummaryWriter writer = memberSummaryWriters.get(CONSTRUCTORS);
 298         addSummary(writer, CONSTRUCTORS, false, memberSummaryTree);
 299     }
 300 
 301     /**
 302      * Build the member summary for the given members.
 303      *
 304      * @param writer the summary writer to write the output.
 305      * @param kind the kind of  members to summarize.
 306      * @param summaryTreeList list of content trees to which the documentation will be added
 307      */
 308     private void buildSummary(MemberSummaryWriter writer,
 309             VisibleMemberTable.Kind kind, LinkedList<Content> summaryTreeList) {
 310         SortedSet<? extends Element> members = asSortedSet(getVisibleMembers(kind));
 311         if (!members.isEmpty()) {
 312             for (Element member : members) {
 313                 final Element property = pHelper.getPropertyElement(member);
 314                 if (property != null) {
 315                     processProperty(member, property);
 316                 }
 317                 List<? extends DocTree> firstSentenceTags = utils.getFirstSentenceTrees(member);
 318                 if (utils.isExecutableElement(member) && firstSentenceTags.isEmpty()) {
 319                     //Inherit comments from overriden or implemented method if
 320                     //necessary.
 321                     DocFinder.Output inheritedDoc =
 322                             DocFinder.search(configuration,
 323                                     new DocFinder.Input(utils, member));
 324                     if (inheritedDoc.holder != null
 325                             && !utils.getFirstSentenceTrees(inheritedDoc.holder).isEmpty()) {
 326                         // let the comment helper know of the overridden element
 327                         CommentHelper ch = utils.getCommentHelper(member);
 328                         ch.setOverrideElement(inheritedDoc.holder);
 329                         firstSentenceTags = utils.getFirstSentenceTrees(inheritedDoc.holder);
 330                     }
 331                 }
 332                 writer.addMemberSummary(typeElement, member, firstSentenceTags);
 333             }
 334             summaryTreeList.add(writer.getSummaryTableTree(typeElement));
 335         }
 336     }
 337 
 338     /**
 339      * Process the property method, property setter and/or property getter
 340      * comment text so that it contains the documentation from
 341      * the property field. The method adds the leading sentence,
 342      * copied documentation including the defaultValue tag and
 343      * the see tags if the appropriate property getter and setter are
 344      * available.
 345      *
 346      * @param member the member which is to be augmented.
 347      * @param property the original property documentation.
 348      */
 349     private void processProperty(Element member,
 350                                  Element property) {
 351         CommentUtils cmtutils = configuration.cmtUtils;
 352         final boolean isSetter = isSetter(member);
 353         final boolean isGetter = isGetter(member);
 354 
 355         List<DocTree> fullBody = new ArrayList<>();
 356         List<DocTree> blockTags = new ArrayList<>();
 357         if (isGetter || isSetter) {
 358             //add "[GS]ets the value of the property PROPERTY_NAME."
 359             if (isSetter) {
 360                 String text = MessageFormat.format(
 361                         resources.getText("doclet.PropertySetterWithName"),
 362                         utils.propertyName((ExecutableElement)member));
 363                 fullBody.addAll(cmtutils.makeFirstSentenceTree(text));
 364             }
 365             if (isGetter) {
 366                 String text = MessageFormat.format(
 367                         resources.getText("doclet.PropertyGetterWithName"),
 368                         utils.propertyName((ExecutableElement) member));
 369                 fullBody.addAll(cmtutils.makeFirstSentenceTree(text));
 370             }
 371             List<? extends DocTree> propertyTags = utils.getBlockTags(property, "propertyDescription");
 372             if (propertyTags.isEmpty()) {
 373                 List<? extends DocTree> comment = utils.getFullBody(property);
 374                 blockTags.addAll(cmtutils.makePropertyDescriptionTree(comment));
 375             }
 376         } else {
 377             fullBody.addAll(utils.getFullBody(property));
 378         }
 379 
 380         // copy certain tags
 381         List<? extends DocTree> tags = utils.getBlockTags(property, Kind.SINCE);
 382         blockTags.addAll(tags);
 383 
 384         List<? extends DocTree> bTags = utils.getBlockTags(property, Kind.UNKNOWN_BLOCK_TAG);
 385         CommentHelper ch = utils.getCommentHelper(property);
 386         for (DocTree dt : bTags) {
 387             String tagName = ch.getTagName(dt);
 388             if ( "defaultValue".equals(tagName)) {
 389                 blockTags.add(dt);
 390             }
 391         }
 392 
 393         //add @see tags
 394         if (!isGetter && !isSetter) {
 395             ExecutableElement getter = pHelper.getGetterForProperty((ExecutableElement)member);
 396             ExecutableElement setter = pHelper.getSetterForProperty((ExecutableElement)member);
 397 
 398             if (null != getter) {
 399                 StringBuilder sb = new StringBuilder("#");
 400                 sb.append(utils.getSimpleName(getter)).append("()");
 401                 blockTags.add(cmtutils.makeSeeTree(sb.toString(), getter));
 402             }
 403 
 404             if (null != setter) {
 405                 VariableElement param = setter.getParameters().get(0);
 406                 StringBuilder sb = new StringBuilder("#");
 407                 sb.append(utils.getSimpleName(setter));
 408                 if (!utils.isTypeVariable(param.asType())) {
 409                     sb.append("(").append(utils.getTypeSignature(param.asType(), false, true)).append(")");
 410                 }
 411                 blockTags.add(cmtutils.makeSeeTree(sb.toString(), setter));
 412             }
 413         }
 414         cmtutils.setDocCommentTree(member, fullBody, blockTags);
 415     }
 416 
 417     /**
 418      * Test whether the method is a getter.
 419      * @param element property method documentation. Needs to be either property
 420      * method, property getter, or property setter.
 421      * @return true if the given documentation belongs to a getter.
 422      */
 423     private boolean isGetter(Element element) {
 424         final String pedName = element.getSimpleName().toString();
 425         return pedName.startsWith("get") || pedName.startsWith("is");
 426     }
 427 
 428     /**
 429      * Test whether the method is a setter.
 430      * @param element property method documentation. Needs to be either property
 431      * method, property getter, or property setter.
 432      * @return true if the given documentation belongs to a setter.
 433      */
 434     private boolean isSetter(Element element) {
 435         return element.getSimpleName().toString().startsWith("set");
 436     }
 437 
 438     /**
 439      * Build the inherited member summary for the given methods.
 440      *
 441      * @param writer the writer for this member summary.
 442      * @param kind the kind of members to document.
 443      * @param summaryTreeList list of content trees to which the documentation will be added
 444      */
 445     private void buildInheritedSummary(MemberSummaryWriter writer,
 446             VisibleMemberTable.Kind kind, LinkedList<Content> summaryTreeList) {
 447         VisibleMemberTable visibleMemberTable = getVisibleMemberTable();
 448         SortedSet<? extends Element> inheritedMembersFromMap = asSortedSet(visibleMemberTable.getAllVisibleMembers(kind));
 449 
 450         for (TypeElement inheritedClass : visibleMemberTable.getVisibleTypeElements()) {
 451             if (!(utils.isPublic(inheritedClass) || utils.isLinkable(inheritedClass))) {
 452                 continue;
 453             }
 454             if (inheritedClass == typeElement) {
 455                 continue;
 456             }
 457 
 458             List<Element> members = inheritedMembersFromMap.stream()
 459                     .filter(e -> utils.getEnclosingTypeElement(e) == inheritedClass)
 460                     .collect(Collectors.toList());
 461             if (!members.isEmpty()) {
 462                 SortedSet<Element> inheritedMembers = new TreeSet<>(comparator);
 463                 inheritedMembers.addAll(members);
 464                 Content inheritedTree = writer.getInheritedSummaryHeader(inheritedClass);
 465                 Content linksTree = writer.getInheritedSummaryLinksTree();
 466                 addSummaryFootNote(inheritedClass, inheritedMembers, linksTree, writer);
 467                 inheritedTree.add(linksTree);
 468                 summaryTreeList.add(inheritedTree);
 469             }
 470         }
 471     }
 472 
 473     private void addSummaryFootNote(TypeElement inheritedClass, SortedSet<Element> inheritedMembers,
 474                                     Content linksTree, MemberSummaryWriter writer) {
 475         for (Element member : inheritedMembers) {
 476             TypeElement t = utils.isUndocumentedEnclosure(inheritedClass)
 477                     ? typeElement : inheritedClass;
 478             writer.addInheritedMemberSummary(t, member, inheritedMembers.first() == member,
 479                     inheritedMembers.last() == member, linksTree);
 480         }
 481     }
 482 
 483     /**
 484      * Add the summary for the documentation.
 485      *
 486      * @param writer the writer for this member summary.
 487      * @param kind the kind of members to document.
 488      * @param showInheritedSummary true if inherited summary should be documented
 489      * @param memberSummaryTree the content tree to which the documentation will be added
 490      */
 491     private void addSummary(MemberSummaryWriter writer,
 492             VisibleMemberTable.Kind kind, boolean showInheritedSummary,
 493             Content memberSummaryTree) {
 494         LinkedList<Content> summaryTreeList = new LinkedList<>();
 495         buildSummary(writer, kind, summaryTreeList);
 496         if (showInheritedSummary)
 497             buildInheritedSummary(writer, kind, summaryTreeList);
 498         if (!summaryTreeList.isEmpty()) {
 499             Content memberTree = writer.getMemberSummaryHeader(typeElement, memberSummaryTree);
 500             summaryTreeList.stream().forEach(memberTree::add);
 501             writer.addMemberTree(memberSummaryTree, memberTree);
 502         }
 503     }
 504 
 505     private SortedSet<? extends Element> asSortedSet(Collection<? extends Element> members) {
 506         SortedSet<Element> out = new TreeSet<>(comparator);
 507         out.addAll(members);
 508         return out;
 509     }
 510 
 511     static class PropertyHelper {
 512 
 513         private final Map<Element, Element> classPropertiesMap = new HashMap<>();
 514 
 515         private final MemberSummaryBuilder  builder;
 516 
 517         PropertyHelper(MemberSummaryBuilder builder) {
 518             this.builder = builder;
 519             computeProperties();
 520         }
 521 
 522         private void computeProperties() {
 523             VisibleMemberTable vmt = builder.getVisibleMemberTable();
 524             List<ExecutableElement> props = ElementFilter.methodsIn(vmt.getVisibleMembers(PROPERTIES));
 525             for (ExecutableElement propertyMethod : props) {
 526                 ExecutableElement getter = vmt.getPropertyGetter(propertyMethod);
 527                 ExecutableElement setter = vmt.getPropertySetter(propertyMethod);
 528                 VariableElement field = vmt.getPropertyField(propertyMethod);
 529 
 530                 addToPropertiesMap(propertyMethod, field, getter, setter);
 531             }
 532         }
 533 
 534         private void addToPropertiesMap(ExecutableElement propertyMethod,
 535                                         VariableElement field,
 536                                         ExecutableElement getter,
 537                                         ExecutableElement setter) {
 538             if (field == null || builder.utils.getDocCommentTree(field) == null) {
 539                 addToPropertiesMap(propertyMethod, propertyMethod);
 540                 addToPropertiesMap(getter, propertyMethod);
 541                 addToPropertiesMap(setter, propertyMethod);
 542             } else {
 543                 addToPropertiesMap(propertyMethod, field);
 544                 addToPropertiesMap(getter, field);
 545                 addToPropertiesMap(setter, field);
 546             }
 547         }
 548 
 549         private void addToPropertiesMap(Element propertyMethod,
 550                                         Element commentSource) {
 551             if (null == propertyMethod || null == commentSource) {
 552                 return;
 553             }
 554             DocCommentTree docTree = builder.utils.getDocCommentTree(propertyMethod);
 555 
 556             /* The second condition is required for the property buckets. In
 557              * this case the comment is at the property method (not at the field)
 558              * and it needs to be listed in the map.
 559              */
 560             if ((docTree == null) || propertyMethod.equals(commentSource)) {
 561                 classPropertiesMap.put(propertyMethod, commentSource);
 562             }
 563         }
 564 
 565         /**
 566          * Returns the property field documentation belonging to the given member.
 567          * @param element the member for which the property documentation is needed.
 568          * @return the property field documentation, null if there is none.
 569          */
 570         public Element getPropertyElement(Element element) {
 571             return classPropertiesMap.get(element);
 572         }
 573 
 574         /**
 575          * Returns the getter documentation belonging to the given property method.
 576          * @param propertyMethod the method for which the getter is needed.
 577          * @return the getter documentation, null if there is none.
 578          */
 579         public ExecutableElement getGetterForProperty(ExecutableElement propertyMethod) {
 580             return builder.getVisibleMemberTable().getPropertyGetter(propertyMethod);
 581         }
 582 
 583         /**
 584          * Returns the setter documentation belonging to the given property method.
 585          * @param propertyMethod the method for which the setter is needed.
 586          * @return the setter documentation, null if there is none.
 587          */
 588         public ExecutableElement getSetterForProperty(ExecutableElement propertyMethod) {
 589             return builder.getVisibleMemberTable().getPropertySetter(propertyMethod);
 590         }
 591     }
 592 }