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