1 /*
2  * Copyright (c) 2003, 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.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      */
MemberSummaryBuilder(Context context, TypeElement typeElement)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      */
getInstance( ClassWriter classWriter, Context context)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      */
getInstance( AnnotationTypeWriter annotationTypeWriter, Context context)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      */
getVisibleMemberTable()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      */
getMemberSummaryWriter(VisibleMemberTable.Kind kind)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      */
members(VisibleMemberTable.Kind kind)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      */
hasMembers(VisibleMemberTable.Kind kind)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      */
buildEnumConstantsSummary(Content memberSummaryTree)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      */
buildAnnotationTypeFieldsSummary(Content memberSummaryTree)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      */
buildAnnotationTypeOptionalMemberSummary(Content memberSummaryTree)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      */
buildAnnotationTypeRequiredMemberSummary(Content memberSummaryTree)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      */
buildFieldsSummary(Content memberSummaryTree)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      */
buildPropertiesSummary(Content memberSummaryTree)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      */
buildNestedClassesSummary(Content memberSummaryTree)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      */
buildMethodsSummary(Content memberSummaryTree)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      */
buildConstructorsSummary(Content memberSummaryTree)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      */
buildSummary(MemberSummaryWriter writer, VisibleMemberTable.Kind kind, LinkedList<Content> summaryTreeList)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, (ExecutableElement) 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      */
processProperty(Element member, Element property)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                         configuration.getText("doclet.PropertySetterWithName"),
362                         utils.propertyName((ExecutableElement)member));
363                 fullBody.addAll(cmtutils.makeFirstSentenceTree(text));
364             }
365             if (isGetter) {
366                 String text = MessageFormat.format(
367                         configuration.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, utils);
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      */
isGetter(Element element)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      */
isSetter(Element element)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      */
buildInheritedSummary(MemberSummaryWriter writer, VisibleMemberTable.Kind kind, LinkedList<Content> summaryTreeList)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.addContent(linksTree);
468                 summaryTreeList.add(writer.getMemberTree(inheritedTree));
469             }
470         }
471     }
472 
addSummaryFootNote(TypeElement inheritedClass, SortedSet<Element> inheritedMembers, Content linksTree, MemberSummaryWriter writer)473     private void addSummaryFootNote(TypeElement inheritedClass, SortedSet<Element> inheritedMembers,
474                                     Content linksTree, MemberSummaryWriter writer) {
475         for (Element member : inheritedMembers) {
476             TypeElement t = (utils.isPackagePrivate(inheritedClass) && !utils.isLinkable(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      */
addSummary(MemberSummaryWriter writer, VisibleMemberTable.Kind kind, boolean showInheritedSummary, Content memberSummaryTree)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::addContent);
501             writer.addMemberTree(memberSummaryTree, memberTree);
502         }
503     }
504 
asSortedSet(Collection<? extends Element> members)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 
PropertyHelper(MemberSummaryBuilder builder)517         PropertyHelper(MemberSummaryBuilder builder) {
518             this.builder = builder;
519             computeProperties();
520         }
521 
computeProperties()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 
addToPropertiesMap(ExecutableElement propertyMethod, VariableElement field, ExecutableElement getter, ExecutableElement setter)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 
addToPropertiesMap(Element propertyMethod, Element commentSource)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          */
getPropertyElement(Element element)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          */
getGetterForProperty(ExecutableElement propertyMethod)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          */
getSetterForProperty(ExecutableElement propertyMethod)588         public ExecutableElement getSetterForProperty(ExecutableElement propertyMethod) {
589             return builder.getVisibleMemberTable().getPropertySetter(propertyMethod);
590         }
591     }
592 }
593