AbstractMemberWriter.java revision 3827:44bdefe64114
1/*
2 * Copyright (c) 1997, 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.formats.html;
27
28import java.util.*;
29import java.util.stream.Collectors;
30
31import javax.lang.model.element.Element;
32import javax.lang.model.element.ExecutableElement;
33import javax.lang.model.element.Modifier;
34import javax.lang.model.element.TypeElement;
35import javax.lang.model.element.TypeParameterElement;
36import javax.lang.model.type.TypeMirror;
37
38import com.sun.source.doctree.DocTree;
39
40import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
41import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants;
42import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
43import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
44import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
45import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
46import jdk.javadoc.internal.doclets.toolkit.Content;
47import jdk.javadoc.internal.doclets.toolkit.Resources;
48import jdk.javadoc.internal.doclets.toolkit.taglets.DeprecatedTaglet;
49import jdk.javadoc.internal.doclets.toolkit.util.MethodTypes;
50import jdk.javadoc.internal.doclets.toolkit.util.Utils;
51import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap;
52
53import static javax.lang.model.element.Modifier.*;
54
55/**
56 * The base class for member writers.
57 *
58 *  <p><b>This is NOT part of any supported API.
59 *  If you write code that depends on this, you do so at your own risk.
60 *  This code and its internal interfaces are subject to change or
61 *  deletion without notice.</b>
62 *
63 * @author Robert Field
64 * @author Atul M Dambalkar
65 * @author Jamie Ho (Re-write)
66 * @author Bhavesh Patel (Modified)
67 */
68public abstract class AbstractMemberWriter {
69
70    protected final ConfigurationImpl configuration;
71    protected final Utils utils;
72    protected final SubWriterHolderWriter writer;
73    protected final Contents contents;
74    protected final Resources resources;
75
76    protected final TypeElement typeElement;
77    protected Map<String, Integer> typeMap = new LinkedHashMap<>();
78    protected Set<MethodTypes> methodTypes = EnumSet.noneOf(MethodTypes.class);
79    private int methodTypesOr = 0;
80    public final boolean nodepr;
81
82    protected boolean printedSummaryHeader = false;
83
84    public AbstractMemberWriter(SubWriterHolderWriter writer, TypeElement typeElement) {
85        this.configuration = writer.configuration;
86        this.writer = writer;
87        this.nodepr = configuration.nodeprecated;
88        this.typeElement = typeElement;
89        this.utils = configuration.utils;
90        this.contents = configuration.contents;
91        this.resources = configuration.resources;
92    }
93
94    public AbstractMemberWriter(SubWriterHolderWriter writer) {
95        this(writer, null);
96    }
97
98    /*** abstracts ***/
99
100    /**
101     * Add the summary label for the member.
102     *
103     * @param memberTree the content tree to which the label will be added
104     */
105    public abstract void addSummaryLabel(Content memberTree);
106
107    /**
108     * Get the summary for the member summary table.
109     *
110     * @return a string for the table summary
111     */
112    public abstract String getTableSummary();
113
114    /**
115     * Get the caption for the member summary table.
116     *
117     * @return a string for the table caption
118     */
119    public abstract Content getCaption();
120
121    /**
122     * Get the summary table header for the member.
123     *
124     * @param member the member to be documented
125     * @return the summary table header
126     */
127    public abstract List<String> getSummaryTableHeader(Element member);
128
129    /**
130     * Add inherited summary label for the member.
131     *
132     * @param typeElement the TypeElement to which to link to
133     * @param inheritedTree the content tree to which the inherited summary label will be added
134     */
135    public abstract void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree);
136
137    /**
138     * Add the anchor for the summary section of the member.
139     *
140     * @param typeElement the TypeElement to be documented
141     * @param memberTree the content tree to which the summary anchor will be added
142     */
143    public abstract void addSummaryAnchor(TypeElement typeElement, Content memberTree);
144
145    /**
146     * Add the anchor for the inherited summary section of the member.
147     *
148     * @param typeElement the TypeElement to be documented
149     * @param inheritedTree the content tree to which the inherited summary anchor will be added
150     */
151    public abstract void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree);
152
153    /**
154     * Add the summary type for the member.
155     *
156     * @param member the member to be documented
157     * @param tdSummaryType the content tree to which the type will be added
158     */
159    protected abstract void addSummaryType(Element member, Content tdSummaryType);
160
161    /**
162     * Add the summary link for the member.
163     *
164     * @param typeElement the TypeElement to be documented
165     * @param member the member to be documented
166     * @param tdSummary the content tree to which the link will be added
167     */
168    protected void addSummaryLink(TypeElement typeElement, Element member, Content tdSummary) {
169        addSummaryLink(LinkInfoImpl.Kind.MEMBER, typeElement, member, tdSummary);
170    }
171
172    /**
173     * Add the summary link for the member.
174     *
175     * @param context the id of the context where the link will be printed
176     * @param typeElement the TypeElement to be documented
177     * @param member the member to be documented
178     * @param tdSummary the content tree to which the summary link will be added
179     */
180    protected abstract void addSummaryLink(LinkInfoImpl.Kind context,
181            TypeElement typeElement, Element member, Content tdSummary);
182
183    /**
184     * Add the inherited summary link for the member.
185     *
186     * @param typeElement the TypeElement to be documented
187     * @param member the member to be documented
188     * @param linksTree the content tree to which the inherited summary link will be added
189     */
190    protected abstract void addInheritedSummaryLink(TypeElement typeElement,
191            Element member, Content linksTree);
192
193    /**
194     * Get the deprecated link.
195     *
196     * @param member the member being linked to
197     * @return a content tree representing the link
198     */
199    protected abstract Content getDeprecatedLink(Element member);
200
201    /**
202     * Get the navigation summary link.
203     *
204     * @param typeElement the TypeElement to be documented
205     * @param link true if its a link else the label to be printed
206     * @return a content tree for the navigation summary link.
207     */
208    protected abstract Content getNavSummaryLink(TypeElement typeElement, boolean link);
209
210    /**
211     * Add the navigation detail link.
212     *
213     * @param link true if its a link else the label to be printed
214     * @param liNav the content tree to which the navigation detail link will be added
215     */
216    protected abstract void addNavDetailLink(boolean link, Content liNav);
217
218    /**
219     * Add the member name to the content tree.
220     *
221     * @param name the member name to be added to the content tree.
222     * @param htmltree the content tree to which the name will be added.
223     */
224    protected void addName(String name, Content htmltree) {
225        htmltree.addContent(name);
226    }
227
228    /**
229     * Add the modifier for the member. The modifiers are ordered as specified
230     * by <em>The Java Language Specification</em>.
231     *
232     * @param member the member for which teh modifier will be added.
233     * @param htmltree the content tree to which the modifier information will be added.
234     */
235    protected void addModifiers(Element member, Content htmltree) {
236        Set<Modifier> set = new TreeSet<>(member.getModifiers());
237
238        // remove the ones we really don't need
239        set.remove(NATIVE);
240        set.remove(SYNCHRONIZED);
241        set.remove(STRICTFP);
242
243        // According to JLS, we should not be showing public modifier for
244        // interface methods.
245        if ((utils.isField(member) || utils.isMethod(member))
246            && ((writer instanceof ClassWriterImpl
247                 && utils.isInterface(((ClassWriterImpl) writer).getTypeElement())  ||
248                 writer instanceof AnnotationTypeWriterImpl) )) {
249            // Remove the implicit abstract and public modifiers
250            if (utils.isMethod(member) &&
251                (utils.isInterface(member.getEnclosingElement()) ||
252                 utils.isAnnotationType(member.getEnclosingElement()))) {
253                set.remove(ABSTRACT);
254                set.remove(PUBLIC);
255            }
256            if (!utils.isMethod(member)) {
257                set.remove(PUBLIC);
258            }
259        }
260        if (!set.isEmpty()) {
261            String mods = set.stream().map(Modifier::toString).collect(Collectors.joining(" "));
262            htmltree.addContent(mods);
263            htmltree.addContent(Contents.SPACE);
264        }
265    }
266
267    protected CharSequence makeSpace(int len) {
268        if (len <= 0) {
269            return "";
270        }
271        StringBuilder sb = new StringBuilder(len);
272        for (int i = 0; i < len; i++) {
273            sb.append(' ');
274        }
275        return sb;
276    }
277
278    /**
279     * Add the modifier and type for the member in the member summary.
280     *
281     * @param member the member to add the type for
282     * @param type the type to add
283     * @param tdSummaryType the content tree to which the modified and type will be added
284     */
285    protected void addModifierAndType(Element member, TypeMirror type,
286            Content tdSummaryType) {
287        HtmlTree code = new HtmlTree(HtmlTag.CODE);
288        addModifier(member, code);
289        if (type == null) {
290            code.addContent(utils.isClass(member) ? "class" : "interface");
291            code.addContent(Contents.SPACE);
292        } else {
293            List<? extends TypeParameterElement> list = utils.isExecutableElement(member)
294                    ? ((ExecutableElement)member).getTypeParameters()
295                    : null;
296            if (list != null && !list.isEmpty()) {
297                Content typeParameters = ((AbstractExecutableMemberWriter) this)
298                        .getTypeParameters((ExecutableElement)member);
299                    code.addContent(typeParameters);
300                //Code to avoid ugly wrapping in member summary table.
301                if (typeParameters.charCount() > 10) {
302                    code.addContent(new HtmlTree(HtmlTag.BR));
303                } else {
304                    code.addContent(Contents.SPACE);
305                }
306                code.addContent(
307                        writer.getLink(new LinkInfoImpl(configuration,
308                        LinkInfoImpl.Kind.SUMMARY_RETURN_TYPE, type)));
309            } else {
310                code.addContent(
311                        writer.getLink(new LinkInfoImpl(configuration,
312                        LinkInfoImpl.Kind.SUMMARY_RETURN_TYPE, type)));
313            }
314
315        }
316        tdSummaryType.addContent(code);
317    }
318
319    /**
320     * Add the modifier for the member.
321     *
322     * @param member the member to add the type for
323     * @param code the content tree to which the modified will be added
324     */
325    private void addModifier(Element member, Content code) {
326        if (utils.isProtected(member)) {
327            code.addContent("protected ");
328        } else if (utils.isPrivate(member)) {
329            code.addContent("private ");
330        } else if (!utils.isPublic(member)) { // Package private
331            code.addContent(configuration.getText("doclet.Package_private"));
332            code.addContent(" ");
333        }
334        boolean isAnnotatedTypeElement = utils.isAnnotationType(member.getEnclosingElement());
335        if (!isAnnotatedTypeElement && utils.isMethod(member)) {
336            if (!utils.isInterface(member.getEnclosingElement()) && utils.isAbstract(member)) {
337                code.addContent("abstract ");
338            }
339            if (utils.isDefault(member)) {
340                code.addContent("default ");
341            }
342        }
343        if (utils.isStatic(member)) {
344            code.addContent("static ");
345        }
346    }
347
348    /**
349     * Add the deprecated information for the given member.
350     *
351     * @param member the member being documented.
352     * @param contentTree the content tree to which the deprecated information will be added.
353     */
354    protected void addDeprecatedInfo(Element member, Content contentTree) {
355        Content output = (new DeprecatedTaglet()).getTagletOutput(member,
356            writer.getTagletWriterInstance(false));
357        if (!output.isEmpty()) {
358            Content deprecatedContent = output;
359            Content div = HtmlTree.DIV(HtmlStyle.block, deprecatedContent);
360            contentTree.addContent(div);
361        }
362    }
363
364    /**
365     * Add the comment for the given member.
366     *
367     * @param member the member being documented.
368     * @param htmltree the content tree to which the comment will be added.
369     */
370    protected void addComment(Element member, Content htmltree) {
371        if (!utils.getFullBody(member).isEmpty()) {
372            writer.addInlineComment(member, htmltree);
373        }
374    }
375
376    protected String name(Element member) {
377        return utils.getSimpleName(member);
378    }
379
380    /**
381     * Get the header for the section.
382     *
383     * @param member the member being documented.
384     * @return a header content for the section.
385     */
386    protected Content getHead(Element member) {
387        Content memberContent = new StringContent(name(member));
388        Content heading = HtmlTree.HEADING(HtmlConstants.MEMBER_HEADING, memberContent);
389        return heading;
390    }
391
392    /**
393    * Return true if the given <code>ProgramElement</code> is inherited
394    * by the class that is being documented.
395    *
396    * @param ped The <code>ProgramElement</code> being checked.
397    * return true if the <code>ProgramElement</code> is being inherited and
398    * false otherwise.
399     *@return true if inherited
400    */
401    protected boolean isInherited(Element ped){
402        return (!utils.isPrivate(ped) &&
403                (!utils.isPackagePrivate(ped) ||
404                    ped.getEnclosingElement().equals(ped.getEnclosingElement())));
405    }
406
407    /**
408     * Add use information to the documentation tree.
409     *
410     * @param mems list of program elements for which the use information will be added
411     * @param heading the section heading
412     * @param tableSummary the summary for the use table
413     * @param contentTree the content tree to which the use information will be added
414     */
415    protected void addUseInfo(List<? extends Element> mems,
416            Content heading, String tableSummary, Content contentTree) {
417        if (mems == null || mems.isEmpty()) {
418            return;
419        }
420        List<? extends Element> members = mems;
421        boolean printedUseTableHeader = false;
422        if (members.size() > 0) {
423            Content caption = writer.getTableCaption(heading);
424            Content table = (configuration.isOutputHtml5())
425                    ? HtmlTree.TABLE(HtmlStyle.useSummary, caption)
426                    : HtmlTree.TABLE(HtmlStyle.useSummary, tableSummary, caption);
427            Content tbody = new HtmlTree(HtmlTag.TBODY);
428            boolean altColor = true;
429            for (Element element : members) {
430                TypeElement te = utils.getEnclosingTypeElement(element);
431                if (!printedUseTableHeader) {
432                    table.addContent(writer.getSummaryTableHeader(
433                            this.getSummaryTableHeader(element), "col"));
434                    printedUseTableHeader = true;
435                }
436                HtmlTree tr = new HtmlTree(HtmlTag.TR);
437                tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
438                altColor = !altColor;
439                HtmlTree tdFirst = new HtmlTree(HtmlTag.TD);
440                tdFirst.addStyle(HtmlStyle.colFirst);
441                writer.addSummaryType(this, element, tdFirst);
442                tr.addContent(tdFirst);
443                HtmlTree thType = new HtmlTree(HtmlTag.TH);
444                thType.addStyle(HtmlStyle.colSecond);
445                thType.addAttr(HtmlAttr.SCOPE, "row");
446                if (te != null
447                        && !utils.isConstructor(element)
448                        && !utils.isClass(element)
449                        && !utils.isInterface(element)
450                        && !utils.isAnnotationType(element)) {
451                    HtmlTree name = new HtmlTree(HtmlTag.SPAN);
452                    name.addStyle(HtmlStyle.typeNameLabel);
453                    name.addContent(name(te) + ".");
454                    thType.addContent(name);
455                }
456                addSummaryLink(utils.isClass(element) || utils.isInterface(element)
457                        ? LinkInfoImpl.Kind.CLASS_USE
458                        : LinkInfoImpl.Kind.MEMBER,
459                        te, element, thType);
460                tr.addContent(thType);
461                HtmlTree tdDesc = new HtmlTree(HtmlTag.TD);
462                tdDesc.addStyle(HtmlStyle.colLast);
463                writer.addSummaryLinkComment(this, element, tdDesc);
464                tr.addContent(tdDesc);
465                tbody.addContent(tr);
466            }
467            table.addContent(tbody);
468            contentTree.addContent(table);
469        }
470    }
471
472    /**
473     * Add the navigation detail link.
474     *
475     * @param members the members to be linked
476     * @param liNav the content tree to which the navigation detail link will be added
477     */
478    protected void addNavDetailLink(SortedSet<Element> members, Content liNav) {
479        addNavDetailLink(!members.isEmpty(), liNav);
480    }
481
482    /**
483     * Add the navigation summary link.
484     *
485     * @param members members to be linked
486     * @param visibleMemberMap the visible inherited members map
487     * @param liNav the content tree to which the navigation summary link will be added
488     */
489    protected void addNavSummaryLink(SortedSet<? extends Element> members,
490            VisibleMemberMap visibleMemberMap, Content liNav) {
491        if (!members.isEmpty()) {
492            liNav.addContent(getNavSummaryLink(null, true));
493            return;
494        }
495
496        TypeElement superClass = utils.getSuperClass(typeElement);
497        while (superClass != null) {
498            if (visibleMemberMap.hasMembers(superClass)) {
499                liNav.addContent(getNavSummaryLink(superClass, true));
500                return;
501            }
502            superClass = utils.getSuperClass(superClass);
503        }
504        liNav.addContent(getNavSummaryLink(null, false));
505    }
506
507    protected void serialWarning(Element e, String key, String a1, String a2) {
508        if (configuration.serialwarn) {
509            configuration.messages.warning(e, key, a1, a2);
510        }
511    }
512
513    /**
514     * Add the member summary for the given class.
515     *
516     * @param tElement the class that is being documented
517     * @param member the member being documented
518     * @param firstSentenceTags the first sentence tags to be added to the summary
519     * @param tableContents the list of contents to which the documentation will be added
520     * @param counter the counter for determining id and style for the table row
521     */
522    public void addMemberSummary(TypeElement tElement, Element member,
523            List<? extends DocTree> firstSentenceTags, List<Content> tableContents, int counter) {
524        HtmlTree tdSummaryType = new HtmlTree(HtmlTag.TD);
525        tdSummaryType.addStyle(HtmlStyle.colFirst);
526        writer.addSummaryType(this, member, tdSummaryType);
527        HtmlTree tr = HtmlTree.TR(tdSummaryType);
528        HtmlTree thSummaryLink = new HtmlTree(HtmlTag.TH);
529        setSummaryColumnStyleAndScope(thSummaryLink);
530        addSummaryLink(tElement, member, thSummaryLink);
531        tr.addContent(thSummaryLink);
532        HtmlTree tdDesc = new HtmlTree(HtmlTag.TD);
533        tdDesc.addStyle(HtmlStyle.colLast);
534        writer.addSummaryLinkComment(this, member, firstSentenceTags, tdDesc);
535        tr.addContent(tdDesc);
536        if (utils.isMethod(member) && !utils.isAnnotationType(member)) {
537            int methodType = utils.isStatic(member) ? MethodTypes.STATIC.value() :
538                    MethodTypes.INSTANCE.value();
539            if (utils.isInterface(member.getEnclosingElement())) {
540                methodType = utils.isAbstract(member)
541                        ? methodType | MethodTypes.ABSTRACT.value()
542                        : methodType | MethodTypes.DEFAULT.value();
543            } else {
544                methodType = utils.isAbstract(member)
545                        ? methodType | MethodTypes.ABSTRACT.value()
546                        : methodType | MethodTypes.CONCRETE.value();
547            }
548            if (utils.isDeprecated(member) || utils.isDeprecated(typeElement)) {
549                methodType = methodType | MethodTypes.DEPRECATED.value();
550            }
551            methodTypesOr = methodTypesOr | methodType;
552            String tableId = "i" + counter;
553            typeMap.put(tableId, methodType);
554            tr.addAttr(HtmlAttr.ID, tableId);
555        }
556        if (counter%2 == 0)
557            tr.addStyle(HtmlStyle.altColor);
558        else
559            tr.addStyle(HtmlStyle.rowColor);
560        tableContents.add(tr);
561    }
562
563    /**
564     * Generate the method types set and return true if the method summary table
565     * needs to show tabs.
566     *
567     * @return true if the table should show tabs
568     */
569    public boolean showTabs() {
570        int value;
571        for (MethodTypes type : EnumSet.allOf(MethodTypes.class)) {
572            value = type.value();
573            if ((value & methodTypesOr) == value) {
574                methodTypes.add(type);
575            }
576        }
577        boolean showTabs = methodTypes.size() > 1;
578        if (showTabs) {
579            methodTypes.add(MethodTypes.ALL);
580        }
581        return showTabs;
582    }
583
584    /**
585     * Set the style and scope attribute for the summary column.
586     *
587     * @param thTree the column for which the style and scope attribute will be set
588     */
589    public void setSummaryColumnStyleAndScope(HtmlTree thTree) {
590        thTree.addStyle(HtmlStyle.colSecond);
591        thTree.addAttr(HtmlAttr.SCOPE, "row");
592    }
593
594    /**
595     * Add inherited member summary for the given class and member.
596     *
597     * @param tElement the class the inherited member belongs to
598     * @param nestedClass the inherited member that is summarized
599     * @param isFirst true if this is the first member in the list
600     * @param isLast true if this is the last member in the list
601     * @param linksTree the content tree to which the summary will be added
602     */
603    public void addInheritedMemberSummary(TypeElement tElement,
604            Element nestedClass, boolean isFirst, boolean isLast,
605            Content linksTree) {
606        writer.addInheritedMemberSummary(this, tElement, nestedClass, isFirst,
607                linksTree);
608    }
609
610    /**
611     * Get the inherited summary header for the given class.
612     *
613     * @param tElement the class the inherited member belongs to
614     * @return a content tree for the inherited summary header
615     */
616    public Content getInheritedSummaryHeader(TypeElement tElement) {
617        Content inheritedTree = writer.getMemberTreeHeader();
618        writer.addInheritedSummaryHeader(this, tElement, inheritedTree);
619        return inheritedTree;
620    }
621
622    /**
623     * Get the inherited summary links tree.
624     *
625     * @return a content tree for the inherited summary links
626     */
627    public Content getInheritedSummaryLinksTree() {
628        return new HtmlTree(HtmlTag.CODE);
629    }
630
631    /**
632     * Get the summary table tree for the given class.
633     *
634     * @param tElement the class for which the summary table is generated
635     * @param tableContents list of contents to be displayed in the summary table
636     * @return a content tree for the summary table
637     */
638    public Content getSummaryTableTree(TypeElement tElement, List<Content> tableContents) {
639        return writer.getSummaryTableTree(this, tElement, tableContents, showTabs());
640    }
641
642    /**
643     * Get the member tree to be documented.
644     *
645     * @param memberTree the content tree of member to be documented
646     * @return a content tree that will be added to the class documentation
647     */
648    public Content getMemberTree(Content memberTree) {
649        return writer.getMemberTree(memberTree);
650    }
651
652    /**
653     * Get the member tree to be documented.
654     *
655     * @param memberTree the content tree of member to be documented
656     * @param isLastContent true if the content to be added is the last content
657     * @return a content tree that will be added to the class documentation
658     */
659    public Content getMemberTree(Content memberTree, boolean isLastContent) {
660        if (isLastContent)
661            return HtmlTree.UL(HtmlStyle.blockListLast, memberTree);
662        else
663            return HtmlTree.UL(HtmlStyle.blockList, memberTree);
664    }
665}
666