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