001/*
002 * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package org.jdrupes.mdoclet.internal.doclets.formats.html;
027
028import static org.jdrupes.mdoclet.internal.doclets.formats.html.HtmlLinkInfo.Kind.LINK_TYPE_PARAMS;
029import static org.jdrupes.mdoclet.internal.doclets.formats.html.HtmlLinkInfo.Kind.LINK_TYPE_PARAMS_AND_BOUNDS;
030import static org.jdrupes.mdoclet.internal.doclets.formats.html.HtmlLinkInfo.Kind.PLAIN;
031import static org.jdrupes.mdoclet.internal.doclets.formats.html.HtmlLinkInfo.Kind.SHOW_PREVIEW;
032import static org.jdrupes.mdoclet.internal.doclets.formats.html.HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_AND_BOUNDS;
033
034import java.util.List;
035
036import javax.lang.model.element.Element;
037import javax.lang.model.element.ElementKind;
038import javax.lang.model.element.ExecutableElement;
039import javax.lang.model.element.TypeElement;
040import javax.lang.model.element.VariableElement;
041import javax.lang.model.type.DeclaredType;
042import javax.lang.model.type.ExecutableType;
043import javax.lang.model.type.TypeMirror;
044import javax.lang.model.util.SimpleTypeVisitor14;
045
046import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.ContentBuilder;
047import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Entity;
048import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlStyle;
049import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlTree;
050import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.TagName;
051import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Text;
052import org.jdrupes.mdoclet.internal.doclets.toolkit.Content;
053
054/**
055 * Print method and constructor info.
056 */
057public abstract class AbstractExecutableMemberWriter
058        extends AbstractMemberWriter {
059
060    public AbstractExecutableMemberWriter(SubWriterHolderWriter writer,
061            TypeElement typeElement) {
062        super(writer, typeElement);
063    }
064
065    public AbstractExecutableMemberWriter(SubWriterHolderWriter writer) {
066        super(writer);
067    }
068
069    /**
070     * Get the type parameters for the executable member.
071     *
072     * @param member the member for which to get the type parameters.
073     * @return the type parameters.
074     */
075    protected Content getTypeParameters(ExecutableElement member) {
076        HtmlLinkInfo linkInfo = new HtmlLinkInfo(configuration,
077            LINK_TYPE_PARAMS_AND_BOUNDS, member)
078                .addLineBreaksInTypeParameters(true)
079                .showTypeParameterAnnotations(true);
080        return writer.getTypeParameterLinks(linkInfo);
081    }
082
083    @Override
084    protected Content getSummaryLink(Element member) {
085        Content content = new ContentBuilder();
086        content.add(utils.getFullyQualifiedName(member));
087        if (!utils.isConstructor(member)) {
088            content.add(".");
089            content.add(member.getSimpleName());
090        }
091        String signature
092            = utils.flatSignature((ExecutableElement) member, typeElement);
093        if (signature.length() > 2) {
094            content.add(new HtmlTree(TagName.WBR));
095        }
096        content.add(signature);
097
098        return writer.getDocLink(SHOW_PREVIEW,
099            utils.getEnclosingTypeElement(member),
100            member, content, null, false);
101    }
102
103    @Override
104    protected void addSummaryLink(HtmlLinkInfo.Kind context, TypeElement te,
105            Element member,
106            Content target) {
107        ExecutableElement ee = (ExecutableElement) member;
108        Content memberLink = writer.getDocLink(context, te, ee, name(ee),
109            HtmlStyle.memberNameLink);
110        var code = HtmlTree.CODE(memberLink);
111        addParameters(ee, code);
112        target.add(code);
113    }
114
115    @Override
116    protected void addInheritedSummaryLink(TypeElement te, Element member,
117            Content target) {
118        target.add(writer.getDocLink(PLAIN, te, member, name(member)));
119    }
120
121    /**
122     * Add the parameter for the executable member.
123     *
124     * @param param the parameter that needs to be added.
125     * @param paramType the type of the parameter.
126     * @param isVarArg true if this is a link to var arg.
127     * @param target the content to which the parameter information will be added.
128     */
129    protected void addParam(VariableElement param, TypeMirror paramType,
130            boolean isVarArg,
131            Content target) {
132        HtmlLinkInfo linkInfo
133            = new HtmlLinkInfo(configuration, LINK_TYPE_PARAMS, paramType)
134                .varargs(isVarArg)
135                .showTypeParameterAnnotations(true);
136        Content link = writer.getLink(linkInfo);
137        target.add(link);
138        if (name(param).length() > 0) {
139            target.add(Entity.NO_BREAK_SPACE);
140            target.add(name(param));
141        }
142    }
143
144    /**
145     * Add the receiver information.
146     *
147     * <p>Note: receivers can only have type-use annotations.</p>
148     *
149     * @param member the member to write receiver annotations for.
150     * @param rcvrType the receiver type.
151     * @param target the content to which the information will be added.
152     */
153    protected void addReceiver(ExecutableElement member, TypeMirror rcvrType,
154            Content target) {
155        var info = new HtmlLinkInfo(configuration, SHOW_TYPE_PARAMS_AND_BOUNDS,
156            rcvrType)
157                .linkToSelf(false);
158        target.add(writer.getLink(info));
159        target.add(Entity.NO_BREAK_SPACE);
160        if (member.getKind() == ElementKind.CONSTRUCTOR) {
161            target.add(utils.getTypeName(rcvrType, false));
162            target.add(".");
163        }
164        target.add("this");
165    }
166
167    /**
168     * Returns {@code true} if a receiver type is annotated anywhere in its type for
169     * inclusion in member details.
170     *
171     * @param receiverType the receiver type.
172     * @return {@code true} if the receiver is annotated
173     */
174    protected boolean isAnnotatedReceiver(TypeMirror receiverType) {
175        return new SimpleTypeVisitor14<Boolean, Void>() {
176            @Override
177            protected Boolean defaultAction(TypeMirror e, Void unused) {
178                return utils.isAnnotated(e);
179            }
180
181            @Override
182            public Boolean visitDeclared(DeclaredType t, Void unused) {
183                if (super.visitDeclared(t, unused)
184                    || visit(t.getEnclosingType())) {
185                    return true;
186                }
187
188                for (var e : t.getTypeArguments()) {
189                    if (visit(e)) {
190                        return true;
191                    }
192                }
193
194                return false;
195            }
196        }.visit(receiverType);
197    }
198
199    /**
200     * Add all the parameters for the executable member.
201     *
202     * @param member the member to write parameters for.
203     * @param target the content to which the parameters information will be added.
204     */
205    protected void addParameters(ExecutableElement member, Content target) {
206        Content params = getParameters(member, false);
207        if (params.charCount() > 2) {
208            // only add <wbr> for non-empty parameters
209            target.add(new HtmlTree(TagName.WBR));
210        }
211        target.add(params);
212    }
213
214    /**
215     * Add all the parameters for the executable member.
216     *
217     * @param member the member to write parameters for.
218     * @param includeAnnotations true if annotation information needs to be added.
219     * @return the parameter information
220     */
221    protected Content getParameters(ExecutableElement member,
222            boolean includeAnnotations) {
223        Content result = new ContentBuilder();
224        result.add("(");
225        String sep = "";
226        List<? extends VariableElement> parameters = member.getParameters();
227        TypeMirror rcvrType = member.getReceiverType();
228        if (includeAnnotations && rcvrType != null
229            && isAnnotatedReceiver(rcvrType)) {
230            addReceiver(member, rcvrType, result);
231            sep = "," + Text.NL + " ";
232        }
233        int paramstart;
234        ExecutableType instMeth
235            = utils.asInstantiatedMethodType(typeElement, member);
236        for (paramstart = 0; paramstart < parameters.size(); paramstart++) {
237            result.add(sep);
238            VariableElement param = parameters.get(paramstart);
239            TypeMirror paramType = instMeth.getParameterTypes().get(paramstart);
240
241            if (param.getKind() != ElementKind.INSTANCE_INIT) {
242                if (includeAnnotations) {
243                    Content annotationInfo
244                        = writer.getAnnotationInfo(param, false);
245                    if (!annotationInfo.isEmpty()) {
246                        result.add(annotationInfo)
247                            .add(Text.NL)
248                            .add(" ");
249                    }
250                }
251                addParam(param, paramType,
252                    (paramstart == parameters.size() - 1) && member.isVarArgs(),
253                    result);
254                break;
255            }
256        }
257
258        for (int i = paramstart + 1; i < parameters.size(); i++) {
259            result.add(",");
260            result.add(Text.NL);
261            result.add(" ");
262
263            if (includeAnnotations) {
264                Content annotationInfo
265                    = writer.getAnnotationInfo(parameters.get(i), false);
266                if (!annotationInfo.isEmpty()) {
267                    result.add(annotationInfo)
268                        .add(Text.NL)
269                        .add(" ");
270                }
271            }
272            addParam(parameters.get(i), instMeth.getParameterTypes().get(i),
273                (i == parameters.size() - 1) && member.isVarArgs(),
274                result);
275        }
276        result.add(")");
277        return result;
278    }
279
280    /**
281     * Get the exception information for the executable member.
282     *
283     * @param member the member to get the exception information for
284     * @return the exception information
285     */
286    protected Content getExceptions(ExecutableElement member) {
287        List<? extends TypeMirror> exceptions = utils
288            .asInstantiatedMethodType(typeElement, member).getThrownTypes();
289        Content result = new ContentBuilder();
290        for (TypeMirror t : exceptions) {
291            if (!result.isEmpty()) {
292                result.add(",");
293                result.add(Text.NL);
294            }
295            Content link
296                = writer.getLink(new HtmlLinkInfo(configuration, PLAIN, t));
297            result.add(link);
298        }
299        return result;
300    }
301
302    protected TypeElement implementsMethodInIntfac(ExecutableElement method,
303            List<TypeElement> intfacs) {
304        for (TypeElement intf : intfacs) {
305            List<ExecutableElement> methods = utils.getMethods(intf);
306            if (!methods.isEmpty()) {
307                for (ExecutableElement md : methods) {
308                    if (name(md).equals(name(method)) &&
309                        md.toString().equals(method.toString())) {
310                        return intf;
311                    }
312                }
313            }
314        }
315        return null;
316    }
317}