001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.tools.plugin.extractor.annotations.converter.tag;
020
021import java.util.Optional;
022import java.util.function.UnaryOperator;
023
024import org.apache.maven.tools.plugin.extractor.annotations.converter.ConverterContext;
025import org.apache.maven.tools.plugin.javadoc.FullyQualifiedJavadocReference;
026import org.apache.maven.tools.plugin.javadoc.JavadocReference;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029
030/**
031 * Utility methods for dealing with links generated from Javadoc tags.
032 */
033public class LinkUtils {
034
035    private static final Logger LOG = LoggerFactory.getLogger(LinkUtils.class);
036
037    private LinkUtils() {
038        // only static methods
039    }
040
041    public static String createLink(String referenceValue, ConverterContext context) {
042        return createLink(referenceValue, context, UnaryOperator.identity());
043    }
044
045    public static String createLink(
046            String referenceValue, ConverterContext context, UnaryOperator<String> labelDecorator) {
047        try {
048            JavadocReference reference = JavadocReference.parse(referenceValue);
049            FullyQualifiedJavadocReference fqReference = context.resolveReference(reference);
050            if (!context.canGetUrl()) {
051                return getReferenceLabel(fqReference, context, labelDecorator, "no javadoc sites associated");
052            }
053            return createLink(referenceValue, fqReference, context, labelDecorator);
054        } catch (IllegalArgumentException e) {
055            LOG.warn(
056                    "Unresolvable link in javadoc tag with value {} found in {}: {}",
057                    referenceValue,
058                    context.getLocation(),
059                    e.getMessage());
060            return labelDecorator.apply(referenceValue) + "<!-- this link could not be resolved -->";
061        }
062    }
063
064    private static String createLink(
065            String referenceValue,
066            FullyQualifiedJavadocReference fqReference,
067            ConverterContext context,
068            UnaryOperator<String> labelDecorator) {
069        StringBuilder link = new StringBuilder();
070        try {
071            link.append("<a href=\"");
072            link.append(context.getUrl(fqReference).toString());
073            link.append("\">");
074            String label = getReferenceLabel(fqReference, context);
075            label = labelDecorator.apply(label);
076            link.append(label);
077            link.append("</a>");
078        } catch (IllegalArgumentException e) {
079            LOG.warn(
080                    "Could not get javadoc URL for reference {} at {} (fully qualified {}): {}",
081                    referenceValue,
082                    fqReference,
083                    context.getLocation(),
084                    e.getMessage());
085            return getReferenceLabel(
086                    fqReference, context, labelDecorator, "reference not found in associated javadoc sites");
087        }
088        return link.toString();
089    }
090
091    private static String getReferenceLabel(
092            FullyQualifiedJavadocReference fqReference,
093            ConverterContext context,
094            UnaryOperator<String> labelDecorator,
095            String htmlComment) {
096        String label = getReferenceLabel(fqReference, context);
097        return labelDecorator.apply(label) + "<!-- " + htmlComment + " -->";
098    }
099
100    /**
101     * @return the undecorated label of the link
102     * @see <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#JSWOR656"> javadoc: How
103     *      a Name Appears</a>
104     */
105    private static String getReferenceLabel(FullyQualifiedJavadocReference fqReference, ConverterContext context) {
106        if (fqReference.getLabel().isPresent()) {
107            return fqReference.getLabel().get();
108        } else {
109            Optional<String> packageName;
110            Optional<String> moduleName;
111            Optional<String> className = fqReference.getClassName();
112            if (Optional.of(context.getPackageName()).equals(fqReference.getPackageName())
113                    && context.getModuleName().equals(fqReference.getModuleName())) {
114                packageName = Optional.empty();
115                moduleName = Optional.empty();
116                if (context.isReferencedBy(fqReference)) {
117                    className = Optional.empty();
118                }
119            } else {
120                packageName = fqReference.getPackageName();
121                moduleName = fqReference.getModuleName();
122            }
123            return createLabel(moduleName, packageName, className, fqReference.getMember());
124        }
125    }
126
127    private static String createLabel(
128            Optional<String> moduleName,
129            Optional<String> packageName,
130            Optional<String> className,
131            Optional<String> member) {
132        StringBuilder sb = new StringBuilder();
133        if (packageName.isPresent() && !"java.lang".equals(packageName.get())) {
134            sb.append(packageName.get());
135        }
136        if (className.isPresent()) {
137            if (sb.length() > 0) {
138                sb.append('.');
139            }
140            sb.append(className.get());
141        }
142        if (member.isPresent()) {
143            if (sb.length() > 0) {
144                sb.append('.');
145            }
146            sb.append(member.get());
147        }
148        return sb.toString();
149    }
150}