[ 
https://issues.apache.org/jira/browse/MPLUGIN-442?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17772825#comment-17772825
 ] 

ASF GitHub Bot commented on MPLUGIN-442:
----------------------------------------

kwin commented on code in PR #225:
URL: 
https://github.com/apache/maven-plugin-tools/pull/225#discussion_r1349527396


##########
maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/GoalRenderer.java:
##########
@@ -0,0 +1,533 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugin.plugin.report;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.MessageFormat;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet.Semantics;
+import org.apache.maven.doxia.util.HtmlTools;
+import org.apache.maven.plugin.descriptor.MojoDescriptor;
+import org.apache.maven.plugin.descriptor.Parameter;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.tools.plugin.EnhancedParameterWrapper;
+import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
+import org.apache.maven.tools.plugin.javadoc.JavadocLinkGenerator;
+import org.apache.maven.tools.plugin.util.PluginUtils;
+import org.codehaus.plexus.i18n.I18N;
+
+public class GoalRenderer extends AbstractPluginReportRenderer {
+
+    /** Regular expression matching an XHTML link with group 1 = link target, 
group 2 = link label. */
+    private static final Pattern HTML_LINK_PATTERN = Pattern.compile("<a 
href=\\\"([^\\\"]*)\\\">(.*?)</a>");
+
+    /** The directory where the generated site is written. Used for resolving 
relative links to javadoc. */
+    private final File reportOutputDirectory;
+
+    private final MojoDescriptor descriptor;
+    private final boolean disableInternalJavadocLinkValidation;
+
+    private final Log log;
+
+    public GoalRenderer(
+            Sink sink,
+            I18N i18n,
+            Locale locale,
+            MavenProject project,
+            MojoDescriptor descriptor,
+            File reportOutputDirectory,
+            boolean disableInternalJavadocLinkValidation,
+            Log log) {
+        super(sink, locale, i18n, project);
+        this.reportOutputDirectory = reportOutputDirectory;
+        this.descriptor = descriptor;
+        this.disableInternalJavadocLinkValidation = 
disableInternalJavadocLinkValidation;
+        this.log = log;
+    }
+
+    @Override
+    public String getTitle() {
+        return descriptor.getFullGoalName();
+    }
+
+    @Override
+    protected void renderBody() {
+        startSection(descriptor.getFullGoalName());
+        renderReportNotice();
+        renderDescription("fullname", descriptor.getPluginDescriptor().getId() 
+ ":" + descriptor.getGoal(), false);
+
+        String context = "goal " + descriptor.getGoal();
+        if (StringUtils.isNotEmpty(descriptor.getDeprecated())) {
+            renderDescription("deprecated", 
getXhtmlWithValidatedLinks(descriptor.getDeprecated(), context), true);
+        }
+        if (StringUtils.isNotEmpty(descriptor.getDescription())) {
+            renderDescription("description", 
getXhtmlWithValidatedLinks(descriptor.getDescription(), context), true);
+        } else {
+            renderDescription("description", getI18nString("nodescription"), 
false);
+        }
+        renderAttributes();
+
+        List<Parameter> parameterList = filterParameters(
+                descriptor.getParameters() != null ? 
descriptor.getParameters() : Collections.emptyList());
+        if (parameterList.isEmpty()) {
+            startSection(getI18nString("parameters"));
+            sink.paragraph();
+            sink.text(getI18nString("noParameter"));
+            sink.paragraph_();
+            endSection();
+        } else {
+            renderParameterOverviewTable(
+                    getI18nString("requiredParameters"),
+                    
parameterList.stream().filter(Parameter::isRequired).iterator());
+            renderParameterOverviewTable(
+                    getI18nString("optionalParameters"),
+                    parameterList.stream().filter(p -> 
!p.isRequired()).iterator());
+            renderParameterDetails(parameterList.iterator());
+        }
+        endSection();
+    }
+
+    /** Filter parameters to only retain those which must be documented, i.e. 
neither components nor read-only ones.
+     *
+     * @param parameterList not null
+     * @return the parameters list without components. */
+    private static List<Parameter> filterParameters(Collection<Parameter> 
parameterList) {
+        return parameterList.stream()
+                .filter(p -> p.isEditable()
+                        && (p.getExpression() == null || 
!p.getExpression().startsWith("${component.")))
+                .collect(Collectors.toList());
+    }
+
+    private void renderReportNotice() {
+        if (PluginUtils.isMavenReport(descriptor.getImplementation(), 
project)) {
+            renderDescription("notice.prefix", 
getI18nString("notice.isMavenReport"), false);
+        }
+    }
+
+    /**
+     * A description consists of a term/prefix and the actual description text
+     */
+    private void renderDescription(String prefixKey, String description, 
boolean isHtmlMarkup) {
+        // TODO: convert to dt and dd elements
+        renderDescriptionPrefix(prefixKey);
+        sink.paragraph();
+        if (isHtmlMarkup) {
+            sink.rawText(description);
+        } else {
+            sink.text(description);
+        }
+        sink.paragraph_(); // p
+    }
+
+    private void renderDescriptionPrefix(String prefixKey) {
+        sink.paragraph();
+        sink.inline(Semantics.STRONG);
+        sink.text(getI18nString(prefixKey));
+        sink.inline_();
+        sink.text(":");
+        sink.paragraph_();
+    }
+
+    @SuppressWarnings("deprecation")
+    private void renderAttributes() {
+        renderDescriptionPrefix("attributes");
+        sink.list();
+
+        renderAttribute(descriptor.isProjectRequired(), "projectRequired");
+        renderAttribute(descriptor.isRequiresReports(), "reportingMojo");
+        renderAttribute(descriptor.isAggregator(), "aggregator");
+        renderAttribute(descriptor.isDirectInvocationOnly(), 
"directInvocationOnly");
+        renderAttribute(descriptor.isDependencyResolutionRequired(), 
"dependencyResolutionRequired");
+
+        if (descriptor instanceof ExtendedMojoDescriptor) {
+            ExtendedMojoDescriptor extendedDescriptor = 
(ExtendedMojoDescriptor) descriptor;
+            
renderAttribute(extendedDescriptor.getDependencyCollectionRequired(), 
"dependencyCollectionRequired");
+        }
+
+        renderAttribute(descriptor.isThreadSafe(), "threadSafe");
+        renderAttribute(!descriptor.isThreadSafe(), "notThreadSafe");
+        renderAttribute(descriptor.getSince(), "since");
+        renderAttribute(descriptor.getPhase(), "phase");
+        renderAttribute(descriptor.getExecutePhase(), "executePhase");
+        renderAttribute(descriptor.getExecuteGoal(), "executeGoal");
+        renderAttribute(descriptor.getExecuteLifecycle(), "executeLifecycle");
+        renderAttribute(descriptor.isOnlineRequired(), "onlineRequired");
+        renderAttribute(!descriptor.isInheritedByDefault(), 
"notInheritedByDefault");
+
+        sink.list_();
+    }
+
+    private void renderAttribute(boolean condition, String attributeKey) {
+        renderAttribute(condition, attributeKey, Optional.empty());
+    }
+
+    private void renderAttribute(String conditionAndCodeArgument, String 
attributeKey) {
+        renderAttribute(
+                StringUtils.isNotEmpty(conditionAndCodeArgument),
+                attributeKey,
+                Optional.ofNullable(conditionAndCodeArgument));
+    }
+
+    private void renderAttribute(boolean condition, String attributeKey, 
Optional<String> codeArgument) {
+        if (condition) {
+            sink.listItem();
+            linkPatternedText(getI18nString(attributeKey));
+            if (codeArgument.isPresent()) {
+                text(": ");
+                sink.inline(Semantics.CODE);
+                sink.text(codeArgument.get());
+                sink.inline_();
+            }
+            text(".");
+            sink.listItem_();
+        }
+    }
+
+    private void renderParameterOverviewTable(String title, 
Iterator<Parameter> parameters) {
+        // don't emit empty tables
+        if (!parameters.hasNext()) {
+            return;
+        }
+        startSection(title);
+        startTable();
+        tableHeader(new String[] {
+            getI18nString("parameter.name.header"),
+            getI18nString("parameter.type.header"),
+            getI18nString("parameter.since.header"),
+            getI18nString("parameter.description.header")
+        });
+        while (parameters.hasNext()) {
+            renderParameterOverviewTableRow(parameters.next());
+        }
+        endTable();
+        endSection();
+    }
+
+    private void renderTableCellWithCode(String text) {
+        renderTableCellWithCode(text, Optional.empty());
+    }
+
+    private void renderTableCellWithCode(String text, Optional<String> link) {
+        sink.tableCell();
+        if (link.isPresent()) {
+            sink.link(link.get(), null);
+        }
+        sink.inline(Semantics.CODE);
+        sink.text(text);
+        sink.inline_();
+        if (link.isPresent()) {
+            sink.link_();
+        }
+        sink.tableCell_();
+    }
+
+    private void renderParameterOverviewTableRow(Parameter parameter) {
+        sink.tableRow();
+        // name
+        // link to appropriate section
+        renderTableCellWithCode(
+                format("parameter.name", parameter.getName()),
+                Optional.of("#" + HtmlTools.encodeId(parameter.getName())));
+
+        // type
+        Map.Entry<String, Optional<String>> type = getLinkedType(parameter, 
true);
+        renderTableCellWithCode(type.getKey(), type.getValue());
+
+        // since
+        String since = StringUtils.defaultIfEmpty(parameter.getSince(), "-");
+        renderTableCellWithCode(since);
+
+        // description
+        sink.tableCell();
+        String description;
+        String context = "Parameter " + parameter.getName() + " in goal " + 
descriptor.getGoal();
+        if (StringUtils.isNotEmpty(parameter.getDeprecated())) {
+            String deprecated = 
getXhtmlWithValidatedLinks(parameter.getDescription(), context);
+            description = format("parameter.deprecated", deprecated);
+        } else if (StringUtils.isNotEmpty(parameter.getDescription())) {
+            description = 
getXhtmlWithValidatedLinks(parameter.getDescription(), context);
+        } else {
+            description = getI18nString("nodescription");
+        }
+        sink.rawText(description);
+        renderTableCellDetail("parameter.defaultValue", 
parameter.getDefaultValue());
+        renderTableCellDetail("parameter.property", 
getPropertyFromExpression(parameter.getExpression()));
+        renderTableCellDetail("parameter.alias", parameter.getAlias());
+        sink.tableCell_();
+
+        sink.tableRow_();
+    }
+
+    private void renderParameterDetails(Iterator<Parameter> parameters) {
+
+        startSection(getI18nString("parameter.details"));
+
+        while (parameters.hasNext()) {
+            Parameter parameter = parameters.next();
+            // deprecated anchor for backwards-compatibility with XDoc (upper 
and lower case)
+            // TODO: remove once migrated to Doxia 2.x
+            sink.anchor(parameter.getName());
+
+            startSection(format("parameter.name", parameter.getName()));
+            sink.anchor_();
+            String context = "Parameter " + parameter.getName() + " in goal " 
+ descriptor.getGoal();
+            if (StringUtils.isNotEmpty(parameter.getDeprecated())) {
+                sink.division();
+                String deprecated = 
getXhtmlWithValidatedLinks(parameter.getDeprecated(), context);
+                sink.rawText(format("parameter.deprecated", deprecated));
+                sink.division_();
+            }
+
+            sink.division();
+            if (StringUtils.isNotEmpty(parameter.getDescription())) {
+                
sink.rawText(getXhtmlWithValidatedLinks(parameter.getDescription(), context));
+            } else {
+                sink.text(getI18nString("nodescription"));
+            }
+            sink.division_();
+
+            sink.list();
+            Map.Entry<String, Optional<String>> typeAndLink = 
getLinkedType(parameter, false);
+            renderDetail(getI18nString("parameter.type"), 
typeAndLink.getKey(), typeAndLink.getValue());
+
+            if (StringUtils.isNotEmpty(parameter.getSince())) {
+                renderDetail(getI18nString("parameter.since"), 
parameter.getSince());
+            }
+
+            if (parameter.isRequired()) {
+                renderDetail(getI18nString("parameter.required"), 
getI18nString("yes"));
+            } else {
+                renderDetail(getI18nString("parameter.required"), 
getI18nString("no"));
+            }
+
+            String expression = parameter.getExpression();
+            String property = getPropertyFromExpression(expression);
+            if (property == null) {
+                renderDetail(getI18nString("parameter.expression"), 
expression);
+            } else {
+                renderDetail(getI18nString("parameter.property"), property);
+            }
+
+            renderDetail(getI18nString("parameter.defaultValue"), 
parameter.getDefaultValue());
+
+            renderDetail(getI18nString("parameter.alias"), 
parameter.getAlias());
+
+            sink.list_(); // ul
+
+            if (parameters.hasNext()) {
+                sink.horizontalRule();
+            }
+            endSection();
+        }
+        endSection();
+    }
+
+    private void renderTableCellDetail(String nameKey, String value) {
+        if (StringUtils.isNotEmpty(value)) {
+            sink.lineBreak();
+            sink.inline(Semantics.STRONG);
+            sink.text(getI18nString(nameKey));
+            sink.inline_();
+            sink.text(": ");
+            sink.inline(Semantics.CODE);
+            sink.text(value);
+            sink.inline_();
+        }
+    }
+
+    private void renderDetail(String param, String value) {
+        renderDetail(param, value, Optional.empty());
+    }
+
+    private void renderDetail(String param, String value, Optional<String> 
valueLink) {
+        if (value != null && !value.isEmpty()) {
+            sink.listItem();
+            sink.inline(Semantics.STRONG);
+            sink.text(param);
+            sink.inline_();
+            sink.text(": ");
+            if (valueLink.isPresent()) {
+                sink.link(valueLink.get());
+            }
+            sink.inline(Semantics.CODE);
+            sink.text(value);
+            sink.inline_();
+            if (valueLink.isPresent()) {
+                sink.link_();
+            }
+            sink.listItem_();
+        }
+    }
+
+    private static String getPropertyFromExpression(String expression) {
+        if ((expression != null && !expression.isEmpty())
+                && expression.startsWith("${")
+                && expression.endsWith("}")
+                && !expression.substring(2).contains("${")) {
+            // expression="${xxx}" -> property="xxx"
+            return expression.substring(2, expression.length() - 1);
+        }
+        // no property can be extracted
+        return null;
+    }
+
+    static String getShortType(String type) {
+        // split into type arguments and main type
+        int startTypeArguments = type.indexOf('<');
+        if (startTypeArguments == -1) {
+            return getShortTypeOfSimpleType(type);
+        } else {
+            StringBuilder shortType = new StringBuilder();
+            shortType.append(getShortTypeOfSimpleType(type.substring(0, 
startTypeArguments)));
+            shortType
+                    .append("<")
+                    
.append(getShortTypeOfTypeArgument(type.substring(startTypeArguments + 1, 
type.lastIndexOf(">"))))
+                    .append(">");
+            return shortType.toString();
+        }
+    }
+
+    private static String getShortTypeOfTypeArgument(String type) {
+        String[] typeArguments = type.split(",\\s*");
+        StringBuilder shortType = new StringBuilder();
+        for (int i = 0; i < typeArguments.length; i++) {
+            String typeArgument = typeArguments[i];
+            if (typeArgument.contains("<")) {
+                // nested type arguments lead to ellipsis
+                return "...";
+            } else {
+                shortType.append(getShortTypeOfSimpleType(typeArgument));
+                if (i < typeArguments.length - 1) {
+                    shortType.append(",");
+                }
+            }
+        }
+        return shortType.toString();
+    }
+
+    private static String getShortTypeOfSimpleType(String type) {
+        int index = type.lastIndexOf('.');
+        return type.substring(index + 1);
+    }
+
+    private Map.Entry<String, Optional<String>> getLinkedType(Parameter 
parameter, boolean isShortType) {
+        final String typeValue;
+        if (isShortType) {
+            typeValue = getShortType(parameter.getType());
+        } else {
+            typeValue = parameter.getType();
+        }
+        URI uri = null;
+        if (parameter instanceof EnhancedParameterWrapper) {
+            EnhancedParameterWrapper enhancedParameter = 
(EnhancedParameterWrapper) parameter;
+            if (enhancedParameter.getTypeJavadocUrl() != null) {
+                URI javadocUrl = enhancedParameter.getTypeJavadocUrl();
+                // optionally check if link is valid
+                if (javadocUrl.isAbsolute()
+                        || disableInternalJavadocLinkValidation
+                        || JavadocLinkGenerator.isLinkValid(javadocUrl, 
reportOutputDirectory.toPath())) {
+                    uri = enhancedParameter.getTypeJavadocUrl();
+                }
+            }
+        }
+        // rely on the decoded link (URL encoding is done by the Sink 
implementation if necessary)
+        Optional<String> link = Optional.ofNullable(uri).map(u -> {
+            StringBuilder decodedLink = new StringBuilder();
+            if (u.getScheme() != null) {
+                decodedLink.append(u.getScheme()).append(":");
+            }
+            decodedLink.append(u.getSchemeSpecificPart());
+            return decodedLink.toString();

Review Comment:
   `URI.toString()` returns the URL-encoded String which is 
   a) not necessary as the URL is encoded by 
https://github.com/apache/maven-doxia/blob/a1e97f9d47b20470c4a5b536406d8c5cb08277bc/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/Xhtml5BaseSink.java#L1595
   b) double encoding potentially leads to invalid links
   
   Therefore it is crucial that the decoded URI (i.e. unencoded link) is passed 
to the Sink API!





> Get rid of deprecated XDoc format
> ---------------------------------
>
>                 Key: MPLUGIN-442
>                 URL: https://issues.apache.org/jira/browse/MPLUGIN-442
>             Project: Maven Plugin Tools
>          Issue Type: Bug
>          Components: Plugin Plugin
>            Reporter: Konrad Windszus
>            Assignee: Konrad Windszus
>            Priority: Major
>
> At some time in the future m-site-p/doxia will no longer support XDoc 
> (compare with 
> https://issues.apache.org/jira/browse/DOXIA-569?focusedCommentId=17634481&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-17634481).
>  Therefore the "report" goal should be converted to create "markdown" for the 
> plugin goal documentation pages instead of "XDoc" in 
> https://github.com/apache/maven-plugin-tools/blob/master/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/PluginXdocGenerator.java.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to