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