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