001/*
002 * Copyright (c) 2003, 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 javax.lang.model.element.Element;
029import javax.lang.model.element.ExecutableElement;
030import javax.lang.model.element.TypeElement;
031import javax.lang.model.type.DeclaredType;
032import javax.lang.model.type.TypeMirror;
033
034import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.ContentBuilder;
035import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlStyle;
036import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Text;
037import org.jdrupes.mdoclet.internal.doclets.toolkit.BaseConfiguration;
038import org.jdrupes.mdoclet.internal.doclets.toolkit.Content;
039import org.jdrupes.mdoclet.internal.doclets.toolkit.util.Utils;
040
041/**
042 * HTML-specific information about a link.
043 */
044public class HtmlLinkInfo {
045
046    /**
047     * Enumeration of different kinds of links.
048     */
049    public enum Kind {
050        /**
051         * Link with just the element name as label.
052         */
053        PLAIN,
054        /**
055         * Link with additional preview flag if appropriate.
056         */
057        SHOW_PREVIEW,
058        /**
059         * Link with optional type parameters appended as plain text.
060         */
061        SHOW_TYPE_PARAMS,
062
063        /**
064         * Link with optional type parameters included in the link label.
065         */
066        SHOW_TYPE_PARAMS_IN_LABEL,
067
068        /**
069         * Link with optional type parameters and bounds appended as plain text.
070         */
071        SHOW_TYPE_PARAMS_AND_BOUNDS,
072        /**
073         * Link with optional type parameters but no bounds rendered as separate links.
074         */
075        LINK_TYPE_PARAMS,
076        /**
077         * Link with optional type parameters and bounds rendered as separate links.
078         */
079        LINK_TYPE_PARAMS_AND_BOUNDS;
080    }
081
082    private final HtmlConfiguration configuration;
083
084    // The context of the link.
085    private Kind context = Kind.PLAIN;
086
087    // The fragment of the link.
088    private String fragment = "";
089
090    // The member this link points to (if any).
091    private Element targetMember;
092
093    // Optional style for the link.
094    private HtmlStyle style = null;
095
096    // The class we want to link to. Null if we are not linking to a class.
097    private TypeElement typeElement;
098
099    // The executable element we want to link to. Null if we are not linking to
100    // an executable element.
101    private ExecutableElement executableElement;
102
103    // The Type we want to link to. Null if we are not linking to a type.
104    private TypeMirror type;
105
106    // True if this is a link to a VarArg.
107    private boolean isVarArg = false;
108
109    // The label for the link.
110    private Content label;
111
112    // True if we should print the type bounds for the type parameter.
113    private boolean showTypeBounds = true;
114
115    // True if type parameters should be rendered as links.
116    private boolean linkTypeParameters = true;
117
118    // By default, the link can be to the page it's already on. However,
119    // there are cases where we don't want this (e.g. heading of class page).
120    private boolean linkToSelf = true;
121
122    // True iff the preview flags should be skipped for this link.
123    private boolean skipPreview;
124
125    // True if type parameters should be separated by hard line breaks.
126    private boolean addLineBreaksInTypeParameters = false;
127
128    // True if additional <wbr> tags should be added to type parameters
129    private boolean addLineBreakOpportunitiesInTypeParameters = false;
130
131    // True if annotations on type parameters should be shown.
132    private boolean showTypeParameterAnnotations = false;
133
134    /**
135     * Construct a LinkInfo object.
136     *
137     * @param configuration the configuration data for the doclet
138     * @param context    the context of the link.
139     * @param ee   the member to link to.
140     */
141    public HtmlLinkInfo(HtmlConfiguration configuration, Kind context,
142            ExecutableElement ee) {
143        this.configuration = configuration;
144        this.executableElement = ee;
145        setContext(context);
146    }
147
148    /**
149     * Construct a LinkInfo object.
150     *
151     * @param configuration the configuration data for the doclet
152     * @param context    the context of the link.
153     * @param typeElement   the class to link to.
154     */
155    public HtmlLinkInfo(HtmlConfiguration configuration, Kind context,
156            TypeElement typeElement) {
157        this.configuration = configuration;
158        this.typeElement = typeElement;
159        setContext(context);
160    }
161
162    /**
163     * Construct a LinkInfo object.
164     *
165     * @param configuration the configuration data for the doclet
166     * @param context    the context of the link.
167     * @param type       the class to link to.
168     */
169    public HtmlLinkInfo(HtmlConfiguration configuration, Kind context,
170            TypeMirror type) {
171        this.configuration = configuration;
172        this.type = type;
173        setContext(context);
174    }
175
176    /**
177     * Creates a copy of this HtmlLinkInfo instance with a different TypeMirror.
178     * This is used for contained types such as type parameters or array components.
179     *
180     * @param type the type mirror
181     * @return the new link info
182     */
183    public HtmlLinkInfo forType(TypeMirror type) {
184        HtmlLinkInfo linkInfo = new HtmlLinkInfo(configuration, context, type);
185        linkInfo.showTypeBounds = showTypeBounds;
186        linkInfo.linkTypeParameters = linkTypeParameters;
187        linkInfo.linkToSelf = linkToSelf;
188        linkInfo.addLineBreaksInTypeParameters = addLineBreaksInTypeParameters;
189        linkInfo.showTypeParameterAnnotations = showTypeParameterAnnotations;
190        linkInfo.skipPreview = skipPreview;
191        return linkInfo;
192    }
193
194    /**
195     * Sets the typeElement
196     * @param typeElement the new typeElement object
197     */
198    public void setTypeElement(TypeElement typeElement) {
199        this.typeElement = typeElement;
200    }
201
202    /**
203     * The class we want to link to.  Null if we are not linking
204     * to a class.
205     */
206    public TypeElement getTypeElement() {
207        return typeElement;
208    }
209
210    /**
211     * The executable element we want to link to.  Null if we are not linking
212     * to an executable element.
213     */
214    public ExecutableElement getExecutableElement() {
215        return executableElement;
216    }
217
218    /**
219     * The Type we want to link to.  Null if we are not linking to a type.
220     */
221    public TypeMirror getType() {
222        return type;
223    }
224
225    /**
226     * Set the label for the link.
227     * @param label plain-text label for the link
228     * @return this object
229     */
230    public HtmlLinkInfo label(CharSequence label) {
231        this.label = Text.of(label);
232        return this;
233    }
234
235    /**
236     * Set the label for the link.
237     * @param label the new value
238     * @return this object
239     */
240    public HtmlLinkInfo label(Content label) {
241        this.label = label;
242        return this;
243    }
244
245    /**
246     * {@return the label for the link}
247     */
248    public Content getLabel() {
249        return label;
250    }
251
252    /**
253     * Sets the style to be used for the link.
254     * @param style the new style value
255     * @return this object
256     */
257    public HtmlLinkInfo style(HtmlStyle style) {
258        this.style = style;
259        return this;
260    }
261
262    /**
263     * {@return the optional style for the link}
264     */
265    public HtmlStyle getStyle() {
266        return style;
267    }
268
269    /**
270     * Set whether or not this is a link to a varargs parameter.
271     * @param varargs the new value
272     * @return this object
273     */
274    public HtmlLinkInfo varargs(boolean varargs) {
275        this.isVarArg = varargs;
276        return this;
277    }
278
279    /**
280     * {@return true if this is a link to a vararg member}
281     */
282    public boolean isVarArg() {
283        return isVarArg;
284    }
285
286    /**
287     * Set the fragment specifier for the link.
288     * @param fragment the new fragment value
289     * @return this object
290     */
291    public HtmlLinkInfo fragment(String fragment) {
292        this.fragment = fragment;
293        return this;
294    }
295
296    /**
297     * {@return the fragment of the link}
298     */
299    public String getFragment() {
300        return fragment;
301    }
302
303    /**
304     * Sets the addLineBreaksInTypeParameters flag for this link.
305     * @param addLineBreaksInTypeParameters the new value
306     * @return this object
307     */
308    public HtmlLinkInfo addLineBreaksInTypeParameters(
309            boolean addLineBreaksInTypeParameters) {
310        this.addLineBreaksInTypeParameters = addLineBreaksInTypeParameters;
311        return this;
312    }
313
314    /**
315     * {@return true if type parameters should be separated by line breaks}
316     */
317    public boolean addLineBreaksInTypeParameters() {
318        return addLineBreaksInTypeParameters;
319    }
320
321    /**
322     * Sets the addLineBreakOpportunitiesInTypeParameters flag for this link.
323     * @param addLineBreakOpportunities the new value
324     * @return this object
325     */
326    public HtmlLinkInfo addLineBreakOpportunitiesInTypeParameters(
327            boolean addLineBreakOpportunities) {
328        this.addLineBreakOpportunitiesInTypeParameters
329            = addLineBreakOpportunities;
330        return this;
331    }
332
333    /**
334     * {@return true if line break opportunities should be added to type parameters}
335     */
336    public boolean addLineBreakOpportunitiesInTypeParameters() {
337        return addLineBreakOpportunitiesInTypeParameters;
338    }
339
340    /**
341     * Set the linkToSelf flag for this link.
342     * @param linkToSelf the new value
343     * @return this object
344     */
345    public HtmlLinkInfo linkToSelf(boolean linkToSelf) {
346        this.linkToSelf = linkToSelf;
347        return this;
348    }
349
350    /**
351     * {@return true if we should generate links to the current page}
352     */
353    public boolean linkToSelf() {
354        return linkToSelf;
355    }
356
357    /**
358     * {@return true if type parameters should be rendered as links}
359     */
360    public boolean linkTypeParameters() {
361        return linkTypeParameters;
362    }
363
364    /**
365     * Set the showTypeBounds flag for this link
366     * @param showTypeBounds the new value
367     */
368    public void showTypeBounds(boolean showTypeBounds) {
369        this.showTypeBounds = showTypeBounds;
370    }
371
372    /**
373     * {@return true if we should print the type bounds for the type parameter}
374     */
375    public boolean showTypeBounds() {
376        return showTypeBounds;
377    }
378
379    /**
380     * Set the showTypeParameterAnnotations flag for this link.
381     * @param showTypeParameterAnnotations the new value
382     * @return this object
383     */
384    public HtmlLinkInfo
385            showTypeParameterAnnotations(boolean showTypeParameterAnnotations) {
386        this.showTypeParameterAnnotations = showTypeParameterAnnotations;
387        return this;
388    }
389
390    /**
391     * {@return true if annotations on type parameters should be shown}
392     */
393    public boolean showTypeParameterAnnotations() {
394        return showTypeParameterAnnotations;
395    }
396
397    /**
398     * Set the member this link points to (if any).
399     * @param el the new member value
400     * @return this object
401     */
402    public HtmlLinkInfo targetMember(Element el) {
403        this.targetMember = el;
404        return this;
405    }
406
407    /**
408     * {@return the member this link points to (if any)}
409     */
410    public Element getTargetMember() {
411        return targetMember;
412    }
413
414    /**
415     * Set whether or not the preview flags should be skipped for this link.
416     * @param skipPreview the new value
417     * @return this object
418     */
419    public HtmlLinkInfo skipPreview(boolean skipPreview) {
420        this.skipPreview = skipPreview;
421        return this;
422    }
423
424    /**
425     * {@return true iff the preview flags should be skipped for this link}
426     */
427    public boolean isSkipPreview() {
428        return skipPreview;
429    }
430
431    /**
432     * {@return the link context}
433     */
434    public Kind getContext() {
435        return context;
436    }
437
438    /**
439     * This method sets the link attributes to the appropriate values
440     * based on the context.
441     *
442     * @param c the context id to set.
443     */
444    private void setContext(Kind c) {
445        linkTypeParameters = c == Kind.LINK_TYPE_PARAMS
446            || c == Kind.LINK_TYPE_PARAMS_AND_BOUNDS;
447        showTypeBounds = c == Kind.SHOW_TYPE_PARAMS_AND_BOUNDS
448            || c == Kind.LINK_TYPE_PARAMS_AND_BOUNDS;
449        context = c;
450    }
451
452    /**
453     * Return true if this link is linkable and false if we can't link to the
454     * desired place.
455     *
456     * @return true if this link is linkable and false if we can't link to the
457     * desired place.
458     */
459    public boolean isLinkable() {
460        return configuration.utils.isLinkable(typeElement);
461    }
462
463    /**
464     * Returns true if links to declared types should include type parameters.
465     *
466     * @return true if type parameter links should be included
467     */
468    public boolean showTypeParameters() {
469        // Type parameters for these kinds of links are either not desired
470        // or already included in the link label.
471        return context != Kind.PLAIN && context != Kind.SHOW_PREVIEW
472            && context != Kind.SHOW_TYPE_PARAMS_IN_LABEL;
473    }
474
475    /**
476     * Return the label for this class link.
477     *
478     * @param configuration the current configuration of the doclet.
479     * @return the label for this class link.
480     */
481    public Content getClassLinkLabel(BaseConfiguration configuration) {
482        if (label != null && !label.isEmpty()) {
483            return label;
484        } else if (isLinkable()) {
485            Content tlabel = newContent();
486            Utils utils = configuration.utils;
487            tlabel.add(type instanceof DeclaredType dt
488                && utils.isGenericType(dt.getEnclosingType())
489                    // If enclosing type is rendered as separate links only use
490                    // own class name
491                    ? typeElement.getSimpleName().toString()
492                    : configuration.utils.getSimpleName(typeElement));
493            return tlabel;
494        } else {
495            Content tlabel = newContent();
496            tlabel.add(configuration.getClassName(typeElement));
497            return tlabel;
498        }
499    }
500
501    /**
502     * {@return a new instance of a content object}
503     */
504    protected Content newContent() {
505        return new ContentBuilder();
506    }
507
508    @Override
509    public String toString() {
510        return "HtmlLinkInfo{" +
511            "typeElement=" + typeElement +
512            ", executableElement=" + executableElement +
513            ", type=" + type +
514            ", isVarArg=" + isVarArg +
515            ", label=" + label +
516            ", showTypeBounds=" + showTypeBounds +
517            ", linkTypeParameters=" + linkTypeParameters +
518            ", linkToSelf=" + linkToSelf +
519            ", addLineBreaksInTypeParameters=" + addLineBreaksInTypeParameters +
520            ", showTypeParameterAnnotations=" + showTypeParameterAnnotations +
521            ", context=" + context +
522            ", fragment=" + fragment +
523            ", style=" + style + '}';
524    }
525}