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