You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by kw...@apache.org on 2023/09/07 16:08:24 UTC

[maven-plugin-tools] 01/01: [MPLUGIN-442] Generate goal documentation leveraging Sink API

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch feature/remove-xdoc-intermediate-format
in repository https://gitbox.apache.org/repos/asf/maven-plugin-tools.git

commit 6e1504ae58a5ad2d808c0c0613f46ce0cfc7c8fb
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Thu Sep 7 18:08:17 2023 +0200

    [MPLUGIN-442] Generate goal documentation leveraging Sink API
    
    Drop XDoc intermediate format
---
 maven-plugin-report-plugin/pom.xml                 |  10 +-
 .../report/AbstractPluginReportRenderer.java       |  54 ++
 .../maven/plugin/plugin/report/GoalRenderer.java   | 543 ++++++++++++++
 .../plugin/report/PluginOverviewRenderer.java      | 392 ++++++++++
 .../maven/plugin/plugin/report/PluginReport.java   | 439 +----------
 .../src/main/resources/plugin-report.properties    |  43 ++
 .../plugin/plugin/report/GoalRendererTest.java     |  64 ++
 maven-plugin-tools-api/pom.xml                     |  19 +
 .../maven/tools/plugin/util/PluginUtils.java       |  54 ++
 .../maven/tools/plugin/util/PluginUtilsTest.java   |  24 +
 .../tools/plugin/util}/stubs/MavenReportStub.java  |   2 +-
 maven-plugin-tools-generators/pom.xml              |  15 -
 .../tools/plugin/generator/GeneratorUtils.java     |  47 +-
 .../plugin/generator/PluginXdocGenerator.java      | 812 ---------------------
 .../src/main/resources/pluginxdoc.properties       |  64 --
 .../src/main/resources/pluginxdoc_de.properties    |  56 --
 .../src/main/resources/pluginxdoc_en.properties    |  23 -
 .../src/main/resources/pluginxdoc_fr.properties    |  60 --
 .../tools/plugin/generator/GeneratorUtilsTest.java |  22 -
 .../plugin/generator/PluginXdocGeneratorTest.java  |  90 ---
 20 files changed, 1222 insertions(+), 1611 deletions(-)

diff --git a/maven-plugin-report-plugin/pom.xml b/maven-plugin-report-plugin/pom.xml
index 04cdf80e..21ad8498 100644
--- a/maven-plugin-report-plugin/pom.xml
+++ b/maven-plugin-report-plugin/pom.xml
@@ -77,10 +77,6 @@
       <groupId>org.apache.maven.plugin-tools</groupId>
       <artifactId>maven-plugin-tools-api</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.apache.maven.plugin-tools</groupId>
-      <artifactId>maven-plugin-tools-generators</artifactId>
-    </dependency>
 
     <!-- doxia -->
     <dependency>
@@ -95,6 +91,12 @@
       <artifactId>maven-reporting-impl</artifactId>
     </dependency>
 
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>3.8.1</version>
+      <!--same version as used by doxia site renderer -->
+    </dependency>
     <!-- plexus -->
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
diff --git a/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/AbstractPluginReportRenderer.java b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/AbstractPluginReportRenderer.java
new file mode 100644
index 00000000..5714939e
--- /dev/null
+++ b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/AbstractPluginReportRenderer.java
@@ -0,0 +1,54 @@
+/*
+ * 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.util.Locale;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.reporting.AbstractMavenReportRenderer;
+import org.codehaus.plexus.i18n.I18N;
+
+/** Base class for all reports generated by the plugin report plugin. Encapsulates i18n functionality as well as functionality to directly
+ * emit XHTML markup */
+public abstract class AbstractPluginReportRenderer extends AbstractMavenReportRenderer {
+
+    private static final String KEY_PREFIX = "report.plugin.";
+
+    private static final String RESOURCE_BASENAME = "plugin-report";
+
+    private final I18N i18n;
+
+    protected final Locale locale;
+
+    protected final MavenProject project;
+
+    protected AbstractPluginReportRenderer(Sink sink, Locale locale, I18N i18n, MavenProject project) {
+        super(sink);
+        this.i18n = i18n;
+        this.locale = locale;
+        this.project = project;
+    }
+
+    /** @param key The key.
+     * @return The translated string. */
+    protected String getI18nString(String key) {
+        return i18n.getString(RESOURCE_BASENAME, locale, KEY_PREFIX + key);
+    }
+}
diff --git a/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/GoalRenderer.java b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/GoalRenderer.java
new file mode 100644
index 00000000..1ef2022d
--- /dev/null
+++ b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/GoalRenderer.java
@@ -0,0 +1,543 @@
+/*
+ * 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.io.IOException;
+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.SinkFactory;
+import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet.Semantics;
+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>");
+
+    public static GoalRenderer create(
+            SinkFactory sinkFactory,
+            File outputDirectory,
+            I18N i18n,
+            Locale locale,
+            MavenProject project,
+            MojoDescriptor descriptor,
+            boolean disableInternalJavadocLinkValidation,
+            Log log)
+            throws IOException {
+        String filename = descriptor.getGoal() + "-mojo.html";
+        Sink sink = sinkFactory.createSink(outputDirectory, filename);
+        return new GoalRenderer(
+                sink, i18n, locale, project, descriptor, outputDirectory, disableInternalJavadocLinkValidation, log);
+    }
+
+    /** 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;
+
+    // only used from tests directly
+    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(
+                "goal.fullname", descriptor.getPluginDescriptor().getId() + ":" + descriptor.getGoal(), false);
+
+        String context = "goal " + descriptor.getGoal();
+        if (StringUtils.isNotEmpty(descriptor.getDeprecated())) {
+            renderDescription("goal.deprecated", getXhtmlWithValidatedLinks(descriptor.getDeprecated(), context), true);
+        }
+        if (StringUtils.isNotEmpty(descriptor.getDescription())) {
+            renderDescription(
+                    "goal.description", getXhtmlWithValidatedLinks(descriptor.getDescription(), context), true);
+        } else {
+            renderDescription("goal.description", getI18nString("goal.nodescription"), false);
+        }
+        renderAttributes();
+
+        List<Parameter> parameterList = filterParameters(
+                descriptor.getParameters() != null ? descriptor.getParameters() : Collections.emptyList());
+        if (parameterList.isEmpty()) {
+            startSection(getI18nString("goal.parameters"));
+            sink.paragraph();
+            sink.text(getI18nString("goal.noParameter"));
+            sink.paragraph_();
+            endSection();
+        } else {
+            renderParameterOverviewTable(
+                    getI18nString("goal.requiredParameters"),
+                    parameterList.stream().filter(Parameter::isRequired).iterator());
+            renderParameterOverviewTable(
+                    getI18nString("goal.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("goal.notice.prefix", getI18nString("goal.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("goal.attributes");
+        sink.list();
+
+        renderAttribute(descriptor.isProjectRequired(), "goal.projectRequired");
+        renderAttribute(descriptor.isRequiresReports(), "goal.reportingMojo");
+        renderAttribute(descriptor.isAggregator(), "goal.aggregator");
+        renderAttribute(descriptor.isDirectInvocationOnly(), "goal.directInvocationOnly");
+        renderAttribute(descriptor.isDependencyResolutionRequired(), "goal.dependencyResolutionRequired");
+
+        if (descriptor instanceof ExtendedMojoDescriptor) {
+            ExtendedMojoDescriptor extendedDescriptor = (ExtendedMojoDescriptor) descriptor;
+            renderAttribute(extendedDescriptor.getDependencyCollectionRequired(), "goal.dependencyCollectionRequired");
+        }
+
+        renderAttribute(descriptor.isThreadSafe(), "goal.threadSafe");
+        renderAttribute(!descriptor.isThreadSafe(), "goal.notThreadSafe");
+        renderAttribute(descriptor.getSince(), "goal.since");
+        renderAttribute(descriptor.getPhase(), "goal.phase");
+        renderAttribute(descriptor.getExecutePhase(), "goal.executePhase");
+        renderAttribute(descriptor.getExecuteGoal(), "goal.executeGoal");
+        renderAttribute(descriptor.getExecuteLifecycle(), "goal.executeLifecycle");
+        renderAttribute(descriptor.isOnlineRequired(), "goal.onlineRequired");
+        renderAttribute(!descriptor.isInheritedByDefault(), "goal.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("goal.parameter.name.header"),
+            getI18nString("goal.parameter.type.header"),
+            getI18nString("goal.parameter.since.header"),
+            getI18nString("goal.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, URI link) {
+        renderTableCellWithCode(text, Optional.of(link));
+    }
+
+    private void renderTableCellWithCode(String text, Optional<URI> link) {
+        sink.tableCell();
+        sink.inline(Semantics.CODE);
+        if (link.isPresent()) {
+            sink.link(link.get().toString(), null);
+        }
+        sink.text(text);
+        if (link.isPresent()) {
+            sink.link_();
+        }
+        sink.inline_();
+        sink.tableCell_();
+    }
+
+    private void renderParameterOverviewTableRow(Parameter parameter) {
+        sink.tableRow();
+        // name
+        try {
+            renderTableCellWithCode(
+                    format("goal.parameter.name", parameter.getName()), new URI(null, null, parameter.getName()));
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException("Cannot create fragment link from " + parameter.getName(), e);
+        }
+
+        // type
+        Map.Entry<String, Optional<URI>> 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("goal.parameter.deprecated", deprecated);
+        } else if (StringUtils.isNotEmpty(parameter.getDescription())) {
+            description = getXhtmlWithValidatedLinks(parameter.getDescription(), context);
+        } else {
+            description = getI18nString("goal.nodescription");
+        }
+        sink.rawText(description);
+        renderTableCellDetail("goal.parameter.defaultValue", parameter.getDefaultValue());
+        renderTableCellDetail("goal.parameter.property", getPropertyFromExpression(parameter.getExpression()));
+        renderTableCellDetail("goal.parameter.alias", parameter.getAlias());
+        sink.tableCell_();
+
+        sink.tableRow_();
+    }
+
+    private void renderParameterDetails(Iterator<Parameter> parameters) {
+
+        startSection(getI18nString("goal.parameter.details"));
+
+        while (parameters.hasNext()) {
+            Parameter parameter = parameters.next();
+            // TODO: rework parameter, currently 3 are generated: one from skin, one from startSection (all lowercase) and one manually
+            sink.anchor(parameter.getName());
+            startSection(format("goal.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("goal.parameter.deprecated", deprecated));
+                sink.division_();
+            }
+
+            sink.division();
+            if (StringUtils.isNotEmpty(parameter.getDescription())) {
+                sink.rawText(getXhtmlWithValidatedLinks(parameter.getDescription(), context));
+            } else {
+                sink.text(getI18nString("goal.nodescription"));
+            }
+            sink.division_();
+
+            sink.list();
+            Map.Entry<String, Optional<URI>> typeAndLink = getLinkedType(parameter, false);
+            renderDetail(getI18nString("goal.parameter.type"), typeAndLink.getKey(), typeAndLink.getValue());
+
+            if (StringUtils.isNotEmpty(parameter.getSince())) {
+                renderDetail(getI18nString("goal.parameter.since"), parameter.getSince());
+            }
+
+            if (parameter.isRequired()) {
+                renderDetail(getI18nString("goal.parameter.required"), getI18nString("yes"));
+            } else {
+                renderDetail(getI18nString("goal.parameter.required"), getI18nString("no"));
+            }
+
+            String expression = parameter.getExpression();
+            String property = getPropertyFromExpression(expression);
+            if (property == null) {
+                renderDetail(getI18nString("goal.parameter.expression"), expression);
+            } else {
+                renderDetail(getI18nString("goal.parameter.property"), property);
+            }
+
+            renderDetail(getI18nString("goal.parameter.defaultValue"), parameter.getDefaultValue());
+
+            renderDetail(getI18nString("goal.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<URI> 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().toString());
+            }
+            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<URI>> 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();
+                }
+            }
+        }
+        return new SimpleEntry<>(typeValue, Optional.ofNullable(uri));
+    }
+
+    String getXhtmlWithValidatedLinks(String xhtmlText, String context) {
+        if (disableInternalJavadocLinkValidation) {
+            return xhtmlText;
+        }
+        StringBuffer sanitizedXhtmlText = new StringBuffer();
+        // find all links which are not absolute
+        Matcher matcher = HTML_LINK_PATTERN.matcher(xhtmlText);
+        while (matcher.find()) {
+            URI link;
+            try {
+                link = new URI(matcher.group(1));
+                if (!link.isAbsolute() && !JavadocLinkGenerator.isLinkValid(link, reportOutputDirectory.toPath())) {
+                    matcher.appendReplacement(sanitizedXhtmlText, matcher.group(2));
+                    log.debug(String.format("Removed invalid link %s in %s", link, context));
+                } else {
+                    matcher.appendReplacement(sanitizedXhtmlText, matcher.group(0));
+                }
+            } catch (URISyntaxException e) {
+                log.warn(String.format(
+                        "Invalid URI %s found in %s. Cannot validate, leave untouched", matcher.group(1), context));
+                matcher.appendReplacement(sanitizedXhtmlText, matcher.group(0));
+            }
+        }
+        matcher.appendTail(sanitizedXhtmlText);
+        return sanitizedXhtmlText.toString();
+    }
+
+    /** Convenience method.
+     *
+     * @param key  not null
+     * @param arg1 not null
+     * @return Localized, formatted text identified by <code>key</code>.
+     * @see #format(String, Object[]) */
+    private String format(String key, Object arg1) {
+        return format(key, new Object[] {arg1});
+    }
+
+    /** Looks up the value for <code>key</code> in the <code>ResourceBundle</code>, then formats that value for the specified
+     * <code>Locale</code> using <code>args</code>.
+     *
+     * @param key  not null
+     * @param args not null
+     * @return Localized, formatted text identified by <code>key</code>. */
+    private String format(String key, Object[] args) {
+        String pattern = getI18nString(key);
+        // we don't need quoting so spare us the confusion in the resource bundle to double them up in some keys
+        pattern = StringUtils.replace(pattern, "'", "''");
+
+        MessageFormat messageFormat = new MessageFormat(pattern, locale);
+        return messageFormat.format(args);
+    }
+}
diff --git a/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginOverviewRenderer.java b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginOverviewRenderer.java
new file mode 100644
index 00000000..fd4c3e26
--- /dev/null
+++ b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginOverviewRenderer.java
@@ -0,0 +1,392 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.doxia.markup.Markup;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.Prerequisites;
+import org.apache.maven.plugin.descriptor.MojoDescriptor;
+import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.shared.utils.xml.Xpp3Dom;
+import org.apache.maven.tools.plugin.ExtendedPluginDescriptor;
+import org.apache.maven.tools.plugin.util.PluginUtils;
+import org.codehaus.plexus.i18n.I18N;
+
+/**
+ * Generates an overview page with the list of goals
+ * and a link to the goal's page.
+ */
+class PluginOverviewRenderer extends AbstractPluginReportRenderer {
+
+    private final List<RequirementsHistory> requirementsHistories;
+
+    private final PluginDescriptor pluginDescriptor;
+
+    private final boolean hasExtensionsToLoad;
+
+    /**
+     * @param sink                  not null
+     * @param i18n                  not null
+     * @param locale                not null
+     * @param project               not null
+     * @param requirementsHistories not null
+     * @param pluginDescriptor      not null
+     */
+    PluginOverviewRenderer(
+            Sink sink,
+            I18N i18n,
+            Locale locale,
+            MavenProject project,
+            List<RequirementsHistory> requirementsHistories,
+            PluginDescriptor pluginDescriptor,
+            boolean hasExtensionsToLoad) {
+        super(sink, locale, i18n, project);
+
+        this.requirementsHistories = requirementsHistories;
+
+        this.pluginDescriptor = pluginDescriptor;
+
+        this.hasExtensionsToLoad = hasExtensionsToLoad;
+    }
+
+    @Override
+    public String getTitle() {
+        return getI18nString("title");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void renderBody() {
+        startSection(getTitle());
+
+        if (!(pluginDescriptor.getMojos() != null && pluginDescriptor.getMojos().size() > 0)) {
+            paragraph(getI18nString("goals.nogoal"));
+            endSection();
+            return;
+        }
+
+        paragraph(getI18nString("goals.intro"));
+
+        boolean hasMavenReport = false;
+        for (MojoDescriptor mojo : pluginDescriptor.getMojos()) {
+            if (PluginUtils.isMavenReport(mojo.getImplementation(), project)) {
+                hasMavenReport = true;
+            }
+        }
+
+        startTable();
+
+        String goalColumnName = getI18nString("goals.column.goal");
+        String isMavenReport = getI18nString("goals.column.isMavenReport");
+        String descriptionColumnName = getI18nString("goals.column.description");
+        if (hasMavenReport) {
+            tableHeader(new String[] {goalColumnName, isMavenReport, descriptionColumnName});
+        } else {
+            tableHeader(new String[] {goalColumnName, descriptionColumnName});
+        }
+
+        List<MojoDescriptor> mojos = new ArrayList<>();
+        mojos.addAll(pluginDescriptor.getMojos());
+        PluginUtils.sortMojos(mojos);
+        for (MojoDescriptor mojo : mojos) {
+            String goalName = mojo.getFullGoalName();
+
+            /*
+             * Added ./ to define a relative path
+             * @see AbstractMavenReportRenderer#getValidHref(java.lang.String)
+             */
+            String goalDocumentationLink = "./" + mojo.getGoal() + "-mojo.html";
+
+            String description;
+            if (StringUtils.isNotEmpty(mojo.getDeprecated())) {
+                description = "<strong>" + getI18nString("goal.deprecated") + "</strong> " + mojo.getDeprecated();
+            } else if (StringUtils.isNotEmpty(mojo.getDescription())) {
+                description = mojo.getDescription();
+            } else {
+                description = getI18nString("goal.nodescription");
+            }
+
+            sink.tableRow();
+            tableCell(createLinkPatternedText(goalName, goalDocumentationLink));
+            if (hasMavenReport) {
+                if (PluginUtils.isMavenReport(mojo.getImplementation(), project)) {
+                    tableCell(getI18nString("isReport"));
+                } else {
+                    tableCell(getI18nString("isNotReport"));
+                }
+            }
+            tableCell(description, true);
+            sink.tableRow_();
+        }
+
+        endTable();
+
+        startSection(getI18nString("systemrequirements"));
+
+        paragraph(getI18nString("systemrequirements.intro"));
+
+        startTable();
+
+        String maven = discoverMavenRequirement(project, pluginDescriptor);
+        sink.tableRow();
+        tableCell(getI18nString("systemrequirements.maven"));
+        tableCell((maven != null ? maven : getI18nString("systemrequirements.nominimum")));
+        sink.tableRow_();
+
+        String jdk = discoverJdkRequirement(project, pluginDescriptor);
+        sink.tableRow();
+        tableCell(getI18nString("systemrequirements.jdk"));
+        tableCell((jdk != null ? jdk : getI18nString("systemrequirements.nominimum")));
+        sink.tableRow_();
+
+        endTable();
+
+        endSection();
+
+        renderRequirementsHistories();
+
+        renderUsageSection(hasMavenReport);
+
+        endSection();
+    }
+
+    private void renderRequirementsHistories() {
+        if (requirementsHistories.isEmpty()) {
+            return;
+        }
+
+        startSection(getI18nString("systemrequirements.history"));
+        paragraph(getI18nString("systemrequirements.history.intro"));
+
+        startTable();
+        tableHeader(new String[] {
+            getI18nString("systemrequirements.history.version"),
+            getI18nString("systemrequirements.history.maven"),
+            getI18nString("systemrequirements.history.jdk")
+        });
+
+        requirementsHistories.forEach(requirementsHistory -> {
+            sink.tableRow();
+            tableCell(requirementsHistory.getVersion());
+            tableCell(requirementsHistory.getMaven());
+            tableCell(requirementsHistory.getJdk());
+            sink.tableRow_();
+        });
+        endTable();
+
+        endSection();
+    }
+
+    /**
+     * Render the section about the usage of the plugin.
+     *
+     * @param hasMavenReport If the plugin has a report or not
+     */
+    private void renderUsageSection(boolean hasMavenReport) {
+        startSection(getI18nString("usage"));
+
+        // Configuration
+        paragraph(getI18nString("usage.intro"));
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("<project>").append(Markup.EOL);
+        sb.append("  ...").append(Markup.EOL);
+        sb.append("  <build>").append(Markup.EOL);
+        sb.append("    <!-- " + getI18nString("usage.pluginManagement") + " -->")
+                .append(Markup.EOL);
+        sb.append("    <pluginManagement>").append(Markup.EOL);
+        sb.append("      <plugins>").append(Markup.EOL);
+        sb.append("        <plugin>").append(Markup.EOL);
+        sb.append("          <groupId>")
+                .append(pluginDescriptor.getGroupId())
+                .append("</groupId>")
+                .append(Markup.EOL);
+        sb.append("          <artifactId>")
+                .append(pluginDescriptor.getArtifactId())
+                .append("</artifactId>")
+                .append(Markup.EOL);
+        sb.append("          <version>")
+                .append(pluginDescriptor.getVersion())
+                .append("</version>")
+                .append(Markup.EOL);
+        if (hasExtensionsToLoad) {
+            sb.append("          <extensions>true</extensions>").append(Markup.EOL);
+        }
+        sb.append("        </plugin>").append(Markup.EOL);
+        sb.append("        ...").append(Markup.EOL);
+        sb.append("      </plugins>").append(Markup.EOL);
+        sb.append("    </pluginManagement>").append(Markup.EOL);
+        sb.append("    <!-- " + getI18nString("usage.plugins") + " -->").append(Markup.EOL);
+        sb.append("    <plugins>").append(Markup.EOL);
+        sb.append("      <plugin>").append(Markup.EOL);
+        sb.append("        <groupId>")
+                .append(pluginDescriptor.getGroupId())
+                .append("</groupId>")
+                .append(Markup.EOL);
+        sb.append("        <artifactId>")
+                .append(pluginDescriptor.getArtifactId())
+                .append("</artifactId>")
+                .append(Markup.EOL);
+        sb.append("      </plugin>").append(Markup.EOL);
+        sb.append("      ...").append(Markup.EOL);
+        sb.append("    </plugins>").append(Markup.EOL);
+        sb.append("  </build>").append(Markup.EOL);
+
+        if (hasMavenReport) {
+            sb.append("  ...").append(Markup.EOL);
+            sb.append("  <!-- " + getI18nString("usage.reporting") + " -->").append(Markup.EOL);
+            sb.append("  <reporting>").append(Markup.EOL);
+            sb.append("    <plugins>").append(Markup.EOL);
+            sb.append("      <plugin>").append(Markup.EOL);
+            sb.append("        <groupId>")
+                    .append(pluginDescriptor.getGroupId())
+                    .append("</groupId>")
+                    .append(Markup.EOL);
+            sb.append("        <artifactId>")
+                    .append(pluginDescriptor.getArtifactId())
+                    .append("</artifactId>")
+                    .append(Markup.EOL);
+            sb.append("        <version>")
+                    .append(pluginDescriptor.getVersion())
+                    .append("</version>")
+                    .append(Markup.EOL);
+            sb.append("      </plugin>").append(Markup.EOL);
+            sb.append("      ...").append(Markup.EOL);
+            sb.append("    </plugins>").append(Markup.EOL);
+            sb.append("  </reporting>").append(Markup.EOL);
+        }
+
+        sb.append("  ...").append(Markup.EOL);
+        sb.append("</project>");
+
+        verbatimText(sb.toString());
+
+        sink.paragraph();
+        linkPatternedText(getI18nString("configuration.end"));
+        sink.paragraph_();
+
+        endSection();
+    }
+
+    /**
+     * Tries to determine the Maven requirement from either the plugin descriptor or (if not set) from the
+     * Maven prerequisites element in the POM.
+     *
+     * @param project      not null
+     * @param pluginDescriptor the plugin descriptor (not null)
+     * @return the Maven version or null if not specified
+     */
+    private static String discoverMavenRequirement(MavenProject project, PluginDescriptor pluginDescriptor) {
+        if (StringUtils.isNotBlank(pluginDescriptor.getRequiredMavenVersion())) {
+            return pluginDescriptor.getRequiredMavenVersion();
+        }
+        return Optional.ofNullable(project.getPrerequisites())
+                .map(Prerequisites::getMaven)
+                .orElse(null);
+    }
+
+    /**
+     * Tries to determine the JDK requirement from the following sources (until one is found)
+     * <ol>
+     * <li>use JDK requirement from plugin descriptor</li>
+     * <li>use {@code release} configuration of {@code org.apache.maven.plugins:maven-compiler-plugin}</li>
+     * <li>use {@code maven.compiler.release<} property</li>
+     * <li>use {@code target} configuration of {@code org.apache.maven.plugins:maven-compiler-plugin}</li>
+     * <li>use {@code maven.compiler.target} property</li>
+     * </ol>
+     *
+     * @param project      not null
+     * @param pluginDescriptor the plugin descriptor (not null)
+     * @return the JDK version
+     */
+    private static String discoverJdkRequirement(MavenProject project, PluginDescriptor pluginDescriptor) {
+        String jdk = null;
+        if (pluginDescriptor instanceof ExtendedPluginDescriptor) {
+            ExtendedPluginDescriptor extPluginDescriptor = (ExtendedPluginDescriptor) pluginDescriptor;
+            jdk = extPluginDescriptor.getRequiredJavaVersion();
+        }
+        if (jdk != null) {
+            return jdk;
+        }
+        Plugin compiler = getCompilerPlugin(project.getBuild().getPluginsAsMap());
+        if (compiler == null) {
+            compiler = getCompilerPlugin(project.getPluginManagement().getPluginsAsMap());
+        }
+
+        jdk = getPluginParameter(compiler, "release");
+        if (jdk != null) {
+            return jdk;
+        }
+
+        jdk = project.getProperties().getProperty("maven.compiler.release");
+        if (jdk != null) {
+            return jdk;
+        }
+
+        jdk = getPluginParameter(compiler, "target");
+        if (jdk != null) {
+            return jdk;
+        }
+
+        // default value
+        jdk = project.getProperties().getProperty("maven.compiler.target");
+        if (jdk != null) {
+            return jdk;
+        }
+
+        String version = (compiler == null) ? null : compiler.getVersion();
+
+        if (version != null) {
+            return "Default target for maven-compiler-plugin version " + version;
+        }
+
+        return null;
+    }
+
+    private static Plugin getCompilerPlugin(Map<String, Plugin> pluginsAsMap) {
+        return pluginsAsMap.get("org.apache.maven.plugins:maven-compiler-plugin");
+    }
+
+    private static String getPluginParameter(Plugin plugin, String parameter) {
+        if (plugin != null) {
+            Xpp3Dom pluginConf = (Xpp3Dom) plugin.getConfiguration();
+
+            if (pluginConf != null) {
+                Xpp3Dom target = pluginConf.getChild(parameter);
+
+                if (target != null) {
+                    return target.getValue();
+                }
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginReport.java b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginReport.java
index c9a17406..06cfbfbe 100644
--- a/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginReport.java
+++ b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginReport.java
@@ -25,14 +25,7 @@ import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
-import java.util.Optional;
 
-import org.apache.maven.doxia.markup.Markup;
-import org.apache.maven.doxia.sink.Sink;
-import org.apache.maven.doxia.tools.SiteTool;
-import org.apache.maven.model.Plugin;
-import org.apache.maven.model.Prerequisites;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
@@ -42,23 +35,12 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.plugin.descriptor.EnhancedPluginDescriptorBuilder;
-import org.apache.maven.project.MavenProject;
 import org.apache.maven.reporting.AbstractMavenReport;
-import org.apache.maven.reporting.AbstractMavenReportRenderer;
 import org.apache.maven.reporting.MavenReportException;
 import org.apache.maven.rtinfo.RuntimeInformation;
-import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
-import org.apache.maven.tools.plugin.ExtendedPluginDescriptor;
-import org.apache.maven.tools.plugin.PluginToolsRequest;
-import org.apache.maven.tools.plugin.generator.GeneratorException;
-import org.apache.maven.tools.plugin.generator.GeneratorUtils;
-import org.apache.maven.tools.plugin.generator.PluginXdocGenerator;
-import org.apache.maven.tools.plugin.util.PluginUtils;
 import org.codehaus.plexus.configuration.PlexusConfigurationException;
 import org.codehaus.plexus.i18n.I18N;
-import org.codehaus.plexus.util.StringUtils;
 import org.codehaus.plexus.util.xml.XmlStreamReader;
-import org.codehaus.plexus.util.xml.Xpp3Dom;
 
 /**
  * Generates the Plugin's documentation report: <code>plugin-info.html</code> plugin overview page,
@@ -72,13 +54,6 @@ import org.codehaus.plexus.util.xml.Xpp3Dom;
 @Mojo(name = "report", threadSafe = true)
 @Execute(phase = LifecyclePhase.PROCESS_CLASSES)
 public class PluginReport extends AbstractMavenReport {
-    /**
-     * Report output directory for mojos' documentation.
-     *
-     * @since 3.7.0
-     */
-    @Parameter(defaultValue = "${project.build.directory}/generated-site")
-    private File generatedSiteDirectory;
 
     /**
      * Set this to "true" to skip generating the report.
@@ -147,15 +122,6 @@ public class PluginReport extends AbstractMavenReport {
     @Parameter(property = "maven.plugin.report.disableInternalJavadocLinkValidation")
     private boolean disableInternalJavadocLinkValidation;
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected String getOutputDirectory() {
-        // PLUGIN-191: output directory of plugin.html, not *-mojo.xml
-        return project.getReporting().getOutputDirectory();
-    }
-
     /**
      * {@inheritDoc}
      */
@@ -229,403 +195,34 @@ public class PluginReport extends AbstractMavenReport {
     }
 
     /**
-     * Generate the mojos documentation, as xdoc files.
+     * Generate the mojos' documentation with the {@link #getSinkFactory()}
      *
      * @param pluginDescriptor not null
      * @param locale           not null
      * @throws MavenReportException if any
+     * @throws IOException
      */
     private void generateMojosDocumentation(PluginDescriptor pluginDescriptor, Locale locale)
             throws MavenReportException {
-        try {
-            File outputDir;
-            if (!locale.equals(SiteTool.DEFAULT_LOCALE)) {
-                outputDir = new File(new File(generatedSiteDirectory, locale.toString()), "xdoc");
-            } else {
-                outputDir = new File(generatedSiteDirectory, "xdoc");
-            }
-            outputDir.mkdirs();
-
-            PluginXdocGenerator generator = new PluginXdocGenerator(
-                    getProject(), locale, getReportOutputDirectory(), disableInternalJavadocLinkValidation);
-            PluginToolsRequest pluginToolsRequest = new DefaultPluginToolsRequest(getProject(), pluginDescriptor);
-            generator.execute(outputDir, pluginToolsRequest);
-        } catch (GeneratorException e) {
-            throw new MavenReportException("Error writing plugin documentation", e);
-        }
-    }
-
-    /**
-     * Generates an overview page with the list of goals
-     * and a link to the goal's page.
-     */
-    static class PluginOverviewRenderer extends AbstractMavenReportRenderer {
-        private final I18N i18n;
-
-        private final Locale locale;
-
-        private final MavenProject project;
-
-        private final List<RequirementsHistory> requirementsHistories;
-
-        private final PluginDescriptor pluginDescriptor;
-
-        private final boolean hasExtensionsToLoad;
-
-        /**
-         * @param sink                  not null
-         * @param i18n                  not null
-         * @param locale                not null
-         * @param project               not null
-         * @param requirementsHistories not null
-         * @param pluginDescriptor      not null
-         */
-        PluginOverviewRenderer(
-                Sink sink,
-                I18N i18n,
-                Locale locale,
-                MavenProject project,
-                List<RequirementsHistory> requirementsHistories,
-                PluginDescriptor pluginDescriptor,
-                boolean hasExtensionsToLoad) {
-            super(sink);
-
-            this.i18n = i18n;
-
-            this.locale = locale;
-
-            this.project = project;
-
-            this.requirementsHistories = requirementsHistories;
-
-            this.pluginDescriptor = pluginDescriptor;
-
-            this.hasExtensionsToLoad = hasExtensionsToLoad;
-        }
-
-        @Override
-        public String getTitle() {
-            return getI18nString("title");
-        }
-
-        /**
-         * @param key The key.
-         * @return The translated string.
-         */
-        protected String getI18nString(String key) {
-            return i18n.getString("plugin-report", locale, "report.plugin." + key);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        protected void renderBody() {
-            startSection(getTitle());
-
-            if (!(pluginDescriptor.getMojos() != null
-                    && pluginDescriptor.getMojos().size() > 0)) {
-                paragraph(getI18nString("goals.nogoal"));
-                endSection();
-                return;
-            }
-
-            paragraph(getI18nString("goals.intro"));
-
-            boolean hasMavenReport = false;
-            for (MojoDescriptor mojo : pluginDescriptor.getMojos()) {
-                if (GeneratorUtils.isMavenReport(mojo.getImplementation(), project)) {
-                    hasMavenReport = true;
-                }
-            }
-
-            startTable();
-
-            String goalColumnName = getI18nString("goals.column.goal");
-            String isMavenReport = getI18nString("goals.column.isMavenReport");
-            String descriptionColumnName = getI18nString("goals.column.description");
-            if (hasMavenReport) {
-                tableHeader(new String[] {goalColumnName, isMavenReport, descriptionColumnName});
-            } else {
-                tableHeader(new String[] {goalColumnName, descriptionColumnName});
-            }
-
-            List<MojoDescriptor> mojos = new ArrayList<>();
-            mojos.addAll(pluginDescriptor.getMojos());
-            PluginUtils.sortMojos(mojos);
-            for (MojoDescriptor mojo : mojos) {
-                String goalName = mojo.getFullGoalName();
-
-                /*
-                 * Added ./ to define a relative path
-                 * @see AbstractMavenReportRenderer#getValidHref(java.lang.String)
-                 */
-                String goalDocumentationLink = "./" + mojo.getGoal() + "-mojo.html";
-
-                String description;
-                if (StringUtils.isNotEmpty(mojo.getDeprecated())) {
-                    description = "<strong>" + getI18nString("goal.deprecated") + "</strong> " + mojo.getDeprecated();
-                } else if (StringUtils.isNotEmpty(mojo.getDescription())) {
-                    description = mojo.getDescription();
-                } else {
-                    description = getI18nString("goal.nodescription");
-                }
-
-                sink.tableRow();
-                tableCell(createLinkPatternedText(goalName, goalDocumentationLink));
-                if (hasMavenReport) {
-                    if (GeneratorUtils.isMavenReport(mojo.getImplementation(), project)) {
-                        tableCell(getI18nString("isReport"));
-                    } else {
-                        tableCell(getI18nString("isNotReport"));
-                    }
-                }
-                tableCell(description, true);
-                sink.tableRow_();
-            }
-
-            endTable();
-
-            startSection(getI18nString("systemrequirements"));
 
-            paragraph(getI18nString("systemrequirements.intro"));
-
-            startTable();
-
-            String maven = discoverMavenRequirement(project, pluginDescriptor);
-            sink.tableRow();
-            tableCell(getI18nString("systemrequirements.maven"));
-            tableCell((maven != null ? maven : getI18nString("systemrequirements.nominimum")));
-            sink.tableRow_();
-
-            String jdk = discoverJdkRequirement(project, pluginDescriptor);
-            sink.tableRow();
-            tableCell(getI18nString("systemrequirements.jdk"));
-            tableCell((jdk != null ? jdk : getI18nString("systemrequirements.nominimum")));
-            sink.tableRow_();
-
-            endTable();
-
-            endSection();
-
-            renderRequirementsHistories();
-
-            renderUsageSection(hasMavenReport);
-
-            endSection();
-        }
-
-        private void renderRequirementsHistories() {
-            if (requirementsHistories.isEmpty()) {
-                return;
-            }
-
-            startSection(getI18nString("systemrequirements.history"));
-            paragraph(getI18nString("systemrequirements.history.intro"));
-
-            startTable();
-            tableHeader(new String[] {
-                getI18nString("systemrequirements.history.version"),
-                getI18nString("systemrequirements.history.maven"),
-                getI18nString("systemrequirements.history.jdk")
-            });
-
-            requirementsHistories.forEach(requirementsHistory -> {
-                sink.tableRow();
-                tableCell(requirementsHistory.getVersion());
-                tableCell(requirementsHistory.getMaven());
-                tableCell(requirementsHistory.getJdk());
-                sink.tableRow_();
-            });
-            endTable();
-
-            endSection();
-        }
-
-        /**
-         * Render the section about the usage of the plugin.
-         *
-         * @param hasMavenReport If the plugin has a report or not
-         */
-        private void renderUsageSection(boolean hasMavenReport) {
-            startSection(getI18nString("usage"));
-
-            // Configuration
-            paragraph(getI18nString("usage.intro"));
-
-            StringBuilder sb = new StringBuilder();
-            sb.append("<project>").append(Markup.EOL);
-            sb.append("  ...").append(Markup.EOL);
-            sb.append("  <build>").append(Markup.EOL);
-            sb.append("    <!-- " + getI18nString("usage.pluginManagement") + " -->")
-                    .append(Markup.EOL);
-            sb.append("    <pluginManagement>").append(Markup.EOL);
-            sb.append("      <plugins>").append(Markup.EOL);
-            sb.append("        <plugin>").append(Markup.EOL);
-            sb.append("          <groupId>")
-                    .append(pluginDescriptor.getGroupId())
-                    .append("</groupId>")
-                    .append(Markup.EOL);
-            sb.append("          <artifactId>")
-                    .append(pluginDescriptor.getArtifactId())
-                    .append("</artifactId>")
-                    .append(Markup.EOL);
-            sb.append("          <version>")
-                    .append(pluginDescriptor.getVersion())
-                    .append("</version>")
-                    .append(Markup.EOL);
-            if (hasExtensionsToLoad) {
-                sb.append("          <extensions>true</extensions>").append(Markup.EOL);
-            }
-            sb.append("        </plugin>").append(Markup.EOL);
-            sb.append("        ...").append(Markup.EOL);
-            sb.append("      </plugins>").append(Markup.EOL);
-            sb.append("    </pluginManagement>").append(Markup.EOL);
-            sb.append("    <!-- " + getI18nString("usage.plugins") + " -->").append(Markup.EOL);
-            sb.append("    <plugins>").append(Markup.EOL);
-            sb.append("      <plugin>").append(Markup.EOL);
-            sb.append("        <groupId>")
-                    .append(pluginDescriptor.getGroupId())
-                    .append("</groupId>")
-                    .append(Markup.EOL);
-            sb.append("        <artifactId>")
-                    .append(pluginDescriptor.getArtifactId())
-                    .append("</artifactId>")
-                    .append(Markup.EOL);
-            sb.append("      </plugin>").append(Markup.EOL);
-            sb.append("      ...").append(Markup.EOL);
-            sb.append("    </plugins>").append(Markup.EOL);
-            sb.append("  </build>").append(Markup.EOL);
-
-            if (hasMavenReport) {
-                sb.append("  ...").append(Markup.EOL);
-                sb.append("  <!-- " + getI18nString("usage.reporting") + " -->").append(Markup.EOL);
-                sb.append("  <reporting>").append(Markup.EOL);
-                sb.append("    <plugins>").append(Markup.EOL);
-                sb.append("      <plugin>").append(Markup.EOL);
-                sb.append("        <groupId>")
-                        .append(pluginDescriptor.getGroupId())
-                        .append("</groupId>")
-                        .append(Markup.EOL);
-                sb.append("        <artifactId>")
-                        .append(pluginDescriptor.getArtifactId())
-                        .append("</artifactId>")
-                        .append(Markup.EOL);
-                sb.append("        <version>")
-                        .append(pluginDescriptor.getVersion())
-                        .append("</version>")
-                        .append(Markup.EOL);
-                sb.append("      </plugin>").append(Markup.EOL);
-                sb.append("      ...").append(Markup.EOL);
-                sb.append("    </plugins>").append(Markup.EOL);
-                sb.append("  </reporting>").append(Markup.EOL);
-            }
-
-            sb.append("  ...").append(Markup.EOL);
-            sb.append("</project>");
-
-            verbatimText(sb.toString());
-
-            sink.paragraph();
-            linkPatternedText(getI18nString("configuration.end"));
-            sink.paragraph_();
-
-            endSection();
-        }
-
-        /**
-         * Tries to determine the Maven requirement from either the plugin descriptor or (if not set) from the
-         * Maven prerequisites element in the POM.
-         *
-         * @param project      not null
-         * @param pluginDescriptor the plugin descriptor (not null)
-         * @return the Maven version or null if not specified
-         */
-        private static String discoverMavenRequirement(MavenProject project, PluginDescriptor pluginDescriptor) {
-            if (StringUtils.isNotBlank(pluginDescriptor.getRequiredMavenVersion())) {
-                return pluginDescriptor.getRequiredMavenVersion();
-            }
-            return Optional.ofNullable(project.getPrerequisites())
-                    .map(Prerequisites::getMaven)
-                    .orElse(null);
-        }
-
-        /**
-         * Tries to determine the JDK requirement from the following sources (until one is found)
-         * <ol>
-         * <li>use JDK requirement from plugin descriptor</li>
-         * <li>use {@code release} configuration of {@code org.apache.maven.plugins:maven-compiler-plugin}</li>
-         * <li>use {@code maven.compiler.release<} property</li>
-         * <li>use {@code target} configuration of {@code org.apache.maven.plugins:maven-compiler-plugin}</li>
-         * <li>use {@code maven.compiler.target} property</li>
-         * </ol>
-         *
-         * @param project      not null
-         * @param pluginDescriptor the plugin descriptor (not null)
-         * @return the JDK version
-         */
-        private static String discoverJdkRequirement(MavenProject project, PluginDescriptor pluginDescriptor) {
-            String jdk = null;
-            if (pluginDescriptor instanceof ExtendedPluginDescriptor) {
-                ExtendedPluginDescriptor extPluginDescriptor = (ExtendedPluginDescriptor) pluginDescriptor;
-                jdk = extPluginDescriptor.getRequiredJavaVersion();
-            }
-            if (jdk != null) {
-                return jdk;
-            }
-            Plugin compiler = getCompilerPlugin(project.getBuild().getPluginsAsMap());
-            if (compiler == null) {
-                compiler = getCompilerPlugin(project.getPluginManagement().getPluginsAsMap());
-            }
-
-            jdk = getPluginParameter(compiler, "release");
-            if (jdk != null) {
-                return jdk;
-            }
-
-            jdk = project.getProperties().getProperty("maven.compiler.release");
-            if (jdk != null) {
-                return jdk;
-            }
-
-            jdk = getPluginParameter(compiler, "target");
-            if (jdk != null) {
-                return jdk;
-            }
-
-            // default value
-            jdk = project.getProperties().getProperty("maven.compiler.target");
-            if (jdk != null) {
-                return jdk;
-            }
-
-            String version = (compiler == null) ? null : compiler.getVersion();
-
-            if (version != null) {
-                return "Default target for maven-compiler-plugin version " + version;
-            }
-
-            return null;
-        }
-
-        private static Plugin getCompilerPlugin(Map<String, Plugin> pluginsAsMap) {
-            return pluginsAsMap.get("org.apache.maven.plugins:maven-compiler-plugin");
-        }
-
-        private static String getPluginParameter(Plugin plugin, String parameter) {
-            if (plugin != null) {
-                Xpp3Dom pluginConf = (Xpp3Dom) plugin.getConfiguration();
-
-                if (pluginConf != null) {
-                    Xpp3Dom target = pluginConf.getChild(parameter);
-
-                    if (target != null) {
-                        return target.getValue();
-                    }
+        if (pluginDescriptor.getMojos() != null) {
+            for (MojoDescriptor descriptor : pluginDescriptor.getMojos()) {
+                GoalRenderer renderer;
+                try {
+                    renderer = GoalRenderer.create(
+                            getSinkFactory(),
+                            getReportOutputDirectory(),
+                            i18n,
+                            locale,
+                            project,
+                            descriptor,
+                            disableInternalJavadocLinkValidation,
+                            getLog());
+                } catch (IOException e) {
+                    throw new MavenReportException("Can not generate sink for mojo " + descriptor.getGoal(), e);
                 }
+                renderer.render();
             }
-
-            return null;
         }
     }
 }
diff --git a/maven-plugin-report-plugin/src/main/resources/plugin-report.properties b/maven-plugin-report-plugin/src/main/resources/plugin-report.properties
index a483e7a0..b2de0ae4 100644
--- a/maven-plugin-report-plugin/src/main/resources/plugin-report.properties
+++ b/maven-plugin-report-plugin/src/main/resources/plugin-report.properties
@@ -28,6 +28,47 @@ report.plugin.goals.column.isMavenReport=Report?
 report.plugin.goals.column.description=Description
 report.plugin.goal.nodescription=No description.
 report.plugin.goal.deprecated=Deprecated.
+report.plugin.goal.notice.prefix=Note
+report.plugin.goal.notice.isMavenReport=This goal should be used as a Maven report.
+report.plugin.goal.fullname=Full name
+report.plugin.goal.description=Description
+
+report.plugin.goal.attributes=Attributes
+report.plugin.goal.projectRequired=Requires a Maven project to be executed
+report.plugin.goal.aggregator=Executes as an aggregator goal
+report.plugin.goal.directInvocationOnly=Executes by direct invocation only
+report.plugin.goal.reportingMojo=Executes only as a reportSet (reporting goal)
+report.plugin.goal.dependencyResolutionRequired=Requires dependency resolution of artifacts in scope
+report.plugin.goal.dependencyCollectionRequired=Requires dependency collection of artifacts in scope
+report.plugin.goal.since=Since version
+report.plugin.goal.phase=Binds by default to the {lifecycle phase, https://maven.apache.org/ref/current/maven-core/lifecycles.html}
+report.plugin.goal.threadSafe=The goal is thread-safe and supports parallel builds
+report.plugin.goal.notThreadSafe=The goal is not marked as thread-safe and thus does not support parallel builds
+report.plugin.goal.executePhase=Invokes the execution of the following {lifecycle phase, https://maven.apache.org/ref/current/maven-core/lifecycles.html} prior to executing itself
+report.plugin.goal.executeGoal=Invokes the execution of the following plugin's goal prior to executing itself
+report.plugin.goal.executeLifecycle=Executes in its own lifecycle
+report.plugin.goal.onlineRequired=Requires that Maven runs in online mode
+report.plugin.goal.notInheritedByDefault=Is NOT inherited by default in multi-project builds
+
+report.plugin.goal.parameters=Parameters
+report.plugin.goal.noParameter=(no parameters)
+report.plugin.goal.requiredParameters=Required Parameters
+report.plugin.goal.optionalParameters=Optional Parameters
+
+report.plugin.goal.parameter.name.header=Name
+report.plugin.goal.parameter.description.header=Description
+report.plugin.goal.parameter.type.header=Type
+report.plugin.goal.parameter.since.header=Since
+report.plugin.goal.parameter.name=<{0}>
+report.plugin.goal.parameter.defaultValue=Default
+report.plugin.goal.parameter.property=User Property
+report.plugin.goal.parameter.alias=Alias
+report.plugin.goal.parameter.deprecated=<strong>Deprecated.</strong> {0}
+report.plugin.goal.parameter.type=Type
+report.plugin.goal.parameter.required=Required
+report.plugin.goal.parameter.expression=Expression
+report.plugin.goal.parameter.details=Parameter Details
+report.plugin.goal.parameter.since=Since
 
 report.plugin.systemrequirements=System Requirements
 report.plugin.systemrequirements.intro=The following specifies the minimum requirements to run this Maven plugin:
@@ -49,3 +90,5 @@ report.plugin.configuration.end=For more information, see {"Guide to Configuring
 
 report.plugin.isReport=Yes
 report.plugin.isNotReport=No
+report.plugin.yes=Yes
+report.plugin.no=No
diff --git a/maven-plugin-report-plugin/src/test/java/org/apache/maven/plugin/plugin/report/GoalRendererTest.java b/maven-plugin-report-plugin/src/test/java/org/apache/maven/plugin/plugin/report/GoalRendererTest.java
new file mode 100644
index 00000000..0801d71e
--- /dev/null
+++ b/maven-plugin-report-plugin/src/test/java/org/apache/maven/plugin/plugin/report/GoalRendererTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.util.Locale;
+
+import org.apache.maven.plugin.logging.SystemStreamLog;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class GoalRendererTest {
+
+    @Test
+    void testGetShortType() {
+        assertEquals("String", GoalRenderer.getShortType("java.lang.String"));
+        assertEquals("List<String>", GoalRenderer.getShortType("java.util.List<java.lang.String>"));
+        assertEquals(
+                "Map<String,Integer>", GoalRenderer.getShortType("java.util.Map<java.lang.String,java.lang.Integer>"));
+        assertEquals("List<...>", GoalRenderer.getShortType("java.util.List<java.util.List<java.lang.String>>"));
+    }
+
+    @Test
+    void testGetXhtmlWithValidatedLinks() {
+        File baseDir = new File(this.getClass().getResource("").getFile());
+        GoalRenderer renderer =
+                new GoalRenderer(null, null, Locale.ROOT, null, null, baseDir, false, new SystemStreamLog());
+        GoalRenderer rendererWithDisabledLinkValidator =
+                new GoalRenderer(null, null, Locale.ROOT, null, null, baseDir, true, new SystemStreamLog());
+        String externalLink =
+                "test<a href=\"http://example.com/test\">External Link</a>..and a second<a href=\"http://localhost/example\">link</a>end";
+        assertEquals(externalLink, renderer.getXhtmlWithValidatedLinks(externalLink, "test"));
+        assertEquals(externalLink, renderer.getXhtmlWithValidatedLinks(externalLink, "test"));
+        String validInternalLink =
+                "test<a href=\"GoalRendererTest.class\">Internal Link</a>..and a second<a href=\"http://localhost/example\">link</a>end";
+        assertEquals(validInternalLink, renderer.getXhtmlWithValidatedLinks(validInternalLink, "test"));
+        assertEquals(validInternalLink, renderer.getXhtmlWithValidatedLinks(validInternalLink, "test"));
+        String invalidInternalLink =
+                "test<a href=\"PluginXdocGeneratorTestinvalid.class\">Internal Link</a>..and a second<a href=\"http://localhost/example\">link</a>end";
+        String sanitizedInvalidInternalLink =
+                "testInternal Link..and a second<a href=\"http://localhost/example\">link</a>end";
+        assertEquals(sanitizedInvalidInternalLink, renderer.getXhtmlWithValidatedLinks(invalidInternalLink, "test"));
+        assertEquals(
+                invalidInternalLink,
+                rendererWithDisabledLinkValidator.getXhtmlWithValidatedLinks(invalidInternalLink, "test"));
+    }
+}
diff --git a/maven-plugin-tools-api/pom.xml b/maven-plugin-tools-api/pom.xml
index 64014d99..7fc54924 100644
--- a/maven-plugin-tools-api/pom.xml
+++ b/maven-plugin-tools-api/pom.xml
@@ -53,6 +53,10 @@
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.maven.reporting</groupId>
+      <artifactId>maven-reporting-api</artifactId>
+    </dependency>
     <!-- plexus -->
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
@@ -96,6 +100,21 @@
       <artifactId>junit-jupiter-params</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.reporting</groupId>
+      <artifactId>maven-reporting-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.plugin-testing</groupId>
+      <artifactId>maven-plugin-testing-harness</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/util/PluginUtils.java b/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/util/PluginUtils.java
index 8ca9e7e4..4aa62ae7 100644
--- a/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/util/PluginUtils.java
+++ b/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/util/PluginUtils.java
@@ -18,14 +18,22 @@
  */
 package org.apache.maven.tools.plugin.util;
 
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.maven.artifact.DependencyResolutionRequiredException;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.reporting.MavenReport;
 import org.codehaus.plexus.util.DirectoryScanner;
 import org.codehaus.plexus.util.FileUtils;
 import org.codehaus.plexus.util.StringUtils;
@@ -137,4 +145,50 @@ public final class PluginUtils {
             });
         }
     }
+
+    /**
+     * @param mojoClassName a fully qualified Mojo implementation class name, not null
+     * @param project a MavenProject instance, could be null
+     * @return <code>true</code> if the Mojo class implements <code>MavenReport</code>,
+     * <code>false</code> otherwise.
+     * @throws IllegalArgumentException if any
+     * @since 3.10.0
+     */
+    public static boolean isMavenReport(String mojoClassName, MavenProject project) throws IllegalArgumentException {
+        if (mojoClassName == null) {
+            throw new IllegalArgumentException("mojo implementation should be declared");
+        }
+
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        if (project != null) {
+            List<String> classPathStrings;
+            try {
+                classPathStrings = project.getCompileClasspathElements();
+                if (project.getExecutionProject() != null) {
+                    classPathStrings.addAll(project.getExecutionProject().getCompileClasspathElements());
+                }
+            } catch (DependencyResolutionRequiredException e) {
+                throw new IllegalArgumentException(e);
+            }
+
+            List<URL> urls = new ArrayList<>(classPathStrings.size());
+            for (String classPathString : classPathStrings) {
+                try {
+                    urls.add(new File(classPathString).toURL());
+                } catch (MalformedURLException e) {
+                    throw new IllegalArgumentException(e);
+                }
+            }
+
+            classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]), classLoader);
+        }
+
+        try {
+            Class<?> clazz = Class.forName(mojoClassName, false, classLoader);
+
+            return MavenReport.class.isAssignableFrom(clazz);
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+    }
 }
diff --git a/maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/util/PluginUtilsTest.java b/maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/util/PluginUtilsTest.java
index 3330f19d..bbd2081e 100644
--- a/maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/util/PluginUtilsTest.java
+++ b/maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/util/PluginUtilsTest.java
@@ -18,10 +18,15 @@
  */
 package org.apache.maven.tools.plugin.util;
 
+import java.util.Collections;
+
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.apache.maven.plugin.testing.stubs.MavenProjectStub;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * @author jdcasey
@@ -61,4 +66,23 @@ class PluginUtilsTest {
         String[] files = PluginUtils.findSources(basedir, includes, excludes);
         assertEquals(1, files.length);
     }
+
+    @Test
+    void testIsMavenReport() throws Exception {
+        try {
+            PluginUtils.isMavenReport(null, null);
+        } catch (IllegalArgumentException e) {
+            assertTrue(true);
+        }
+
+        String impl = "org.apache.maven.tools.plugin.util.stubs.MavenReportStub";
+
+        MavenProjectStub stub = new MavenProjectStub();
+        stub.setCompileSourceRoots(Collections.singletonList(System.getProperty("basedir") + "/target/classes"));
+
+        assertTrue(PluginUtils.isMavenReport(impl, stub));
+
+        impl = "org.apache.maven.tools.plugin.util.stubs.MojoStub";
+        assertFalse(PluginUtils.isMavenReport(impl, stub));
+    }
 }
diff --git a/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/stubs/MavenReportStub.java b/maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/util/stubs/MavenReportStub.java
similarity index 97%
rename from maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/stubs/MavenReportStub.java
rename to maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/util/stubs/MavenReportStub.java
index 855284f8..0358de02 100644
--- a/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/stubs/MavenReportStub.java
+++ b/maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/util/stubs/MavenReportStub.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.maven.tools.plugin.generator.stubs;
+package org.apache.maven.tools.plugin.util.stubs;
 
 import java.util.Locale;
 
diff --git a/maven-plugin-tools-generators/pom.xml b/maven-plugin-tools-generators/pom.xml
index 1198e807..7093d0c3 100644
--- a/maven-plugin-tools-generators/pom.xml
+++ b/maven-plugin-tools-generators/pom.xml
@@ -44,11 +44,6 @@
       <artifactId>maven-model</artifactId>
     </dependency>
 
-    <dependency>
-      <groupId>org.apache.maven.reporting</groupId>
-      <artifactId>maven-reporting-api</artifactId>
-    </dependency>
-
     <!-- plexus -->
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
@@ -98,16 +93,6 @@
       <artifactId>plexus-testing</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>org.apache.maven.reporting</groupId>
-      <artifactId>maven-reporting-impl</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.maven.plugin-testing</groupId>
-      <artifactId>maven-plugin-testing-harness</artifactId>
-      <scope>test</scope>
-    </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-simple</artifactId>
diff --git a/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/GeneratorUtils.java b/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/GeneratorUtils.java
index 99aa7965..c9779870 100644
--- a/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/GeneratorUtils.java
+++ b/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/GeneratorUtils.java
@@ -25,14 +25,9 @@ import javax.swing.text.html.parser.ParserDelegator;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
 import java.io.IOException;
 import java.io.StringReader;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -43,11 +38,10 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.maven.artifact.Artifact;
-import org.apache.maven.artifact.DependencyResolutionRequiredException;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 import org.apache.maven.project.MavenProject;
-import org.apache.maven.reporting.MavenReport;
+import org.apache.maven.tools.plugin.util.PluginUtils;
 import org.codehaus.plexus.component.repository.ComponentDependency;
 import org.codehaus.plexus.util.StringUtils;
 import org.codehaus.plexus.util.xml.XMLWriter;
@@ -557,43 +551,10 @@ public final class GeneratorUtils {
      * @return <code>true</code> is the Mojo implementation implements <code>MavenReport</code>,
      * <code>false</code> otherwise.
      * @throws IllegalArgumentException if any
+     * @deprecated Use {@link PluginUtils#isMavenReport(String, MavenProject)} instead.
      */
-    @SuppressWarnings("unchecked")
+    @Deprecated
     public static boolean isMavenReport(String impl, MavenProject project) throws IllegalArgumentException {
-        if (impl == null) {
-            throw new IllegalArgumentException("mojo implementation should be declared");
-        }
-
-        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-        if (project != null) {
-            List<String> classPathStrings;
-            try {
-                classPathStrings = project.getCompileClasspathElements();
-                if (project.getExecutionProject() != null) {
-                    classPathStrings.addAll(project.getExecutionProject().getCompileClasspathElements());
-                }
-            } catch (DependencyResolutionRequiredException e) {
-                throw new IllegalArgumentException(e);
-            }
-
-            List<URL> urls = new ArrayList<>(classPathStrings.size());
-            for (String classPathString : classPathStrings) {
-                try {
-                    urls.add(new File(classPathString).toURL());
-                } catch (MalformedURLException e) {
-                    throw new IllegalArgumentException(e);
-                }
-            }
-
-            classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]), classLoader);
-        }
-
-        try {
-            Class<?> clazz = Class.forName(impl, false, classLoader);
-
-            return MavenReport.class.isAssignableFrom(clazz);
-        } catch (ClassNotFoundException e) {
-            return false;
-        }
+        return PluginUtils.isMavenReport(impl, project);
     }
 }
diff --git a/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/PluginXdocGenerator.java b/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/PluginXdocGenerator.java
deleted file mode 100644
index 047355c7..00000000
--- a/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/PluginXdocGenerator.java
+++ /dev/null
@@ -1,812 +0,0 @@
-/*
- * 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.tools.plugin.generator;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Writer;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.ResourceBundle;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.maven.plugin.descriptor.MojoDescriptor;
-import org.apache.maven.plugin.descriptor.Parameter;
-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.PluginToolsRequest;
-import org.apache.maven.tools.plugin.javadoc.JavadocLinkGenerator;
-import org.codehaus.plexus.util.StringUtils;
-import org.codehaus.plexus.util.io.CachingOutputStream;
-import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
-import org.codehaus.plexus.util.xml.XMLWriter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-/**
- * Generate <a href="https://maven.apache.org/doxia/references/xdoc-format.html">xdoc documentation</a> for each mojo.
- */
-public class PluginXdocGenerator implements Generator {
-    /**
-     * Regular expression matching an XHTML link
-     * group 1 = link target, group 2 = link label
-     */
-    private static final Pattern HTML_LINK_PATTERN = Pattern.compile("<a href=\\\"([^\\\"]*)\\\">(.*?)</a>");
-
-    private static final Logger LOG = LoggerFactory.getLogger(PluginXdocGenerator.class);
-
-    /**
-     * locale
-     */
-    private final Locale locale;
-
-    /**
-     * project
-     */
-    private final MavenProject project;
-
-    /**
-     * The directory where the generated site is written.
-     * Used for resolving relative links to javadoc.
-     */
-    private final File reportOutputDirectory;
-
-    private final boolean disableInternalJavadocLinkValidation;
-
-    /**
-     * Default constructor using <code>Locale.ENGLISH</code> as locale.
-     * Used only in test cases.
-     */
-    public PluginXdocGenerator() {
-        this(null);
-    }
-
-    /**
-     * Constructor using <code>Locale.ENGLISH</code> as locale.
-     *
-     * @param project not null Maven project.
-     */
-    public PluginXdocGenerator(MavenProject project) {
-        this(project, Locale.ENGLISH, new File("").getAbsoluteFile(), false);
-    }
-
-    /**
-     * @param project not null.
-     * @param locale  not null.
-     */
-    public PluginXdocGenerator(
-            MavenProject project,
-            Locale locale,
-            File reportOutputDirectory,
-            boolean disableInternalJavadocLinkValidation) {
-        this.project = project;
-        this.locale = locale;
-        this.reportOutputDirectory = reportOutputDirectory;
-        this.disableInternalJavadocLinkValidation = disableInternalJavadocLinkValidation;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void execute(File destinationDirectory, PluginToolsRequest request) throws GeneratorException {
-        try {
-            if (request.getPluginDescriptor().getMojos() != null) {
-                List<MojoDescriptor> mojos = request.getPluginDescriptor().getMojos();
-                for (MojoDescriptor descriptor : mojos) {
-                    processMojoDescriptor(descriptor, destinationDirectory);
-                }
-            }
-        } catch (IOException e) {
-            throw new GeneratorException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * @param mojoDescriptor       not null
-     * @param destinationDirectory not null
-     * @throws IOException if any
-     */
-    protected void processMojoDescriptor(MojoDescriptor mojoDescriptor, File destinationDirectory) throws IOException {
-        File outputFile = new File(destinationDirectory, getMojoFilename(mojoDescriptor, "xml"));
-        try (Writer writer = new OutputStreamWriter(new CachingOutputStream(outputFile), UTF_8)) {
-            XMLWriter w = new PrettyPrintXMLWriter(new PrintWriter(writer), UTF_8.name(), null);
-            writeBody(mojoDescriptor, w);
-
-            writer.flush();
-        }
-    }
-
-    /**
-     * @param mojo not null
-     * @param ext  not null
-     * @return the output file name
-     */
-    private String getMojoFilename(MojoDescriptor mojo, String ext) {
-        return mojo.getGoal() + "-mojo." + ext;
-    }
-
-    /**
-     * @param mojoDescriptor not null
-     * @param w              not null
-     */
-    private void writeBody(MojoDescriptor mojoDescriptor, XMLWriter w) {
-        w.startElement("document");
-        w.addAttribute("xmlns", "http://maven.apache.org/XDOC/2.0");
-        w.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
-        w.addAttribute(
-                "xsi:schemaLocation", "http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd");
-
-        // ----------------------------------------------------------------------
-        //
-        // ----------------------------------------------------------------------
-
-        w.startElement("properties");
-
-        w.startElement("title");
-        w.writeText(mojoDescriptor.getFullGoalName());
-        w.endElement(); // title
-
-        w.endElement(); // properties
-
-        // ----------------------------------------------------------------------
-        //
-        // ----------------------------------------------------------------------
-
-        w.startElement("body");
-
-        w.startElement("section");
-
-        w.addAttribute("name", mojoDescriptor.getFullGoalName());
-
-        writeReportNotice(mojoDescriptor, w);
-
-        w.startElement("p");
-        w.writeMarkup(getString("pluginxdoc.mojodescriptor.fullname"));
-        w.endElement(); // p
-        w.startElement("p");
-        w.writeMarkup(mojoDescriptor.getPluginDescriptor().getId() + ":" + mojoDescriptor.getGoal());
-        w.endElement(); // p
-
-        String context = "goal " + mojoDescriptor.getGoal();
-        if (StringUtils.isNotEmpty(mojoDescriptor.getDeprecated())) {
-            w.startElement("p");
-            w.writeMarkup(getString("pluginxdoc.mojodescriptor.deprecated"));
-            w.endElement(); // p
-            w.startElement("div");
-            w.writeMarkup(getXhtmlWithValidatedLinks(mojoDescriptor.getDeprecated(), context));
-            w.endElement(); // div
-        }
-
-        w.startElement("p");
-        w.writeMarkup(getString("pluginxdoc.description"));
-        w.endElement(); // p
-        w.startElement("div");
-        if (StringUtils.isNotEmpty(mojoDescriptor.getDescription())) {
-            w.writeMarkup(getXhtmlWithValidatedLinks(mojoDescriptor.getDescription(), context));
-        } else {
-            w.writeText(getString("pluginxdoc.nodescription"));
-        }
-        w.endElement(); // div
-
-        writeGoalAttributes(mojoDescriptor, w);
-
-        writeGoalParameterTable(mojoDescriptor, w);
-
-        w.endElement(); // section
-
-        w.endElement(); // body
-
-        w.endElement(); // document
-    }
-
-    /**
-     * @param mojoDescriptor not null
-     * @param w              not null
-     */
-    private void writeReportNotice(MojoDescriptor mojoDescriptor, XMLWriter w) {
-        if (GeneratorUtils.isMavenReport(mojoDescriptor.getImplementation(), project)) {
-            w.startElement("p");
-            w.writeMarkup(getString("pluginxdoc.mojodescriptor.notice.note"));
-            w.writeText(getString("pluginxdoc.mojodescriptor.notice.isMavenReport"));
-            w.endElement(); // p
-        }
-    }
-
-    /**
-     * @param mojoDescriptor not null
-     * @param w              not null
-     */
-    private void writeGoalAttributes(MojoDescriptor mojoDescriptor, XMLWriter w) {
-        w.startElement("p");
-        w.writeMarkup(getString("pluginxdoc.mojodescriptor.attributes"));
-        w.endElement(); // p
-
-        boolean addedUl = false;
-        String value;
-        if (mojoDescriptor.isProjectRequired()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(getString("pluginxdoc.mojodescriptor.projectRequired"));
-            w.endElement(); // li
-        }
-
-        if (mojoDescriptor.isRequiresReports()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(getString("pluginxdoc.mojodescriptor.reportingMojo"));
-            w.endElement(); // li
-        }
-
-        if (mojoDescriptor.isAggregator()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(getString("pluginxdoc.mojodescriptor.aggregator"));
-            w.endElement(); // li
-        }
-
-        if (mojoDescriptor.isDirectInvocationOnly()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(getString("pluginxdoc.mojodescriptor.directInvocationOnly"));
-            w.endElement(); // li
-        }
-
-        value = mojoDescriptor.isDependencyResolutionRequired();
-        if (value != null && !value.isEmpty()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(format("pluginxdoc.mojodescriptor.dependencyResolutionRequired", value));
-            w.endElement(); // li
-        }
-
-        if (mojoDescriptor instanceof ExtendedMojoDescriptor) {
-            ExtendedMojoDescriptor extendedMojoDescriptor = (ExtendedMojoDescriptor) mojoDescriptor;
-
-            value = extendedMojoDescriptor.getDependencyCollectionRequired();
-            if (value != null && !value.isEmpty()) {
-                addedUl = addUl(w, addedUl);
-                w.startElement("li");
-                w.writeMarkup(format("pluginxdoc.mojodescriptor.dependencyCollectionRequired", value));
-                w.endElement(); // li
-            }
-        }
-
-        addedUl = addUl(w, addedUl);
-        w.startElement("li");
-        w.writeMarkup(getString(
-                mojoDescriptor.isThreadSafe()
-                        ? "pluginxdoc.mojodescriptor.threadSafe"
-                        : "pluginxdoc.mojodescriptor.notThreadSafe"));
-        w.endElement(); // li
-
-        value = mojoDescriptor.getSince();
-        if (value != null && !value.isEmpty()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(format("pluginxdoc.mojodescriptor.since", value));
-            w.endElement(); // li
-        }
-
-        value = mojoDescriptor.getPhase();
-        if (value != null && !value.isEmpty()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(format("pluginxdoc.mojodescriptor.phase", value));
-            w.endElement(); // li
-        }
-
-        value = mojoDescriptor.getExecutePhase();
-        if (value != null && !value.isEmpty()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(format("pluginxdoc.mojodescriptor.executePhase", value));
-            w.endElement(); // li
-        }
-
-        value = mojoDescriptor.getExecuteGoal();
-        if (value != null && !value.isEmpty()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(format("pluginxdoc.mojodescriptor.executeGoal", value));
-            w.endElement(); // li
-        }
-
-        value = mojoDescriptor.getExecuteLifecycle();
-        if (value != null && !value.isEmpty()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(format("pluginxdoc.mojodescriptor.executeLifecycle", value));
-            w.endElement(); // li
-        }
-
-        if (mojoDescriptor.isOnlineRequired()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(getString("pluginxdoc.mojodescriptor.onlineRequired"));
-            w.endElement(); // li
-        }
-
-        if (!mojoDescriptor.isInheritedByDefault()) {
-            addedUl = addUl(w, addedUl);
-            w.startElement("li");
-            w.writeMarkup(getString("pluginxdoc.mojodescriptor.inheritedByDefault"));
-            w.endElement(); // li
-        }
-
-        if (addedUl) {
-            w.endElement(); // ul
-        }
-    }
-
-    /**
-     * @param mojoDescriptor not null
-     * @param w              not null
-     */
-    private void writeGoalParameterTable(MojoDescriptor mojoDescriptor, XMLWriter w) {
-        List<Parameter> parameterList = mojoDescriptor.getParameters();
-
-        // remove components and read-only parameters
-        List<Parameter> list = filterParameters(parameterList);
-
-        if (!list.isEmpty()) {
-            writeParameterSummary(list, w, mojoDescriptor.getGoal());
-            writeParameterDetails(list, w, mojoDescriptor.getGoal());
-        } else {
-            w.startElement("subsection");
-            w.addAttribute("name", getString("pluginxdoc.mojodescriptor.parameters"));
-
-            w.startElement("p");
-            w.writeMarkup(getString("pluginxdoc.mojodescriptor.noParameter"));
-            w.endElement(); // p
-
-            w.endElement();
-        }
-    }
-
-    /**
-     * Filter parameters to only retain those which must be documented, i.e. neither components nor readonly.
-     *
-     * @param parameterList not null
-     * @return the parameters list without components.
-     */
-    private List<Parameter> filterParameters(List<Parameter> parameterList) {
-        List<Parameter> filtered = new ArrayList<>();
-
-        if (parameterList != null) {
-            for (Parameter parameter : parameterList) {
-                if (parameter.isEditable()) {
-                    String expression = parameter.getExpression();
-
-                    if (expression == null || !expression.startsWith("${component.")) {
-                        filtered.add(parameter);
-                    }
-                }
-            }
-        }
-
-        return filtered;
-    }
-
-    /**
-     * @param parameterList  not null
-     * @param w              not null
-     */
-    private void writeParameterDetails(List<Parameter> parameterList, XMLWriter w, String goal) {
-        w.startElement("subsection");
-        w.addAttribute("name", getString("pluginxdoc.mojodescriptor.parameter.details"));
-
-        for (Iterator<Parameter> parameters = parameterList.iterator(); parameters.hasNext(); ) {
-            Parameter parameter = parameters.next();
-
-            w.startElement("h4");
-            w.writeMarkup(format("pluginxdoc.mojodescriptor.parameter.name_internal", parameter.getName()));
-            w.endElement();
-
-            String context = "Parameter " + parameter.getName() + " in goal " + goal;
-            if (StringUtils.isNotEmpty(parameter.getDeprecated())) {
-                w.startElement("div");
-                String deprecated = getXhtmlWithValidatedLinks(parameter.getDeprecated(), context);
-                w.writeMarkup(format("pluginxdoc.mojodescriptor.parameter.deprecated", deprecated));
-                w.endElement(); // div
-            }
-
-            w.startElement("div");
-            if (StringUtils.isNotEmpty(parameter.getDescription())) {
-                w.writeMarkup(getXhtmlWithValidatedLinks(parameter.getDescription(), context));
-            } else {
-                w.writeMarkup(getString("pluginxdoc.nodescription"));
-            }
-            w.endElement(); // div
-
-            boolean addedUl = false;
-            addedUl = addUl(w, addedUl, parameter.getType());
-            String typeValue = getLinkedType(parameter, false);
-            writeDetail(getString("pluginxdoc.mojodescriptor.parameter.type"), typeValue, w);
-
-            if (StringUtils.isNotEmpty(parameter.getSince())) {
-                addedUl = addUl(w, addedUl);
-                writeDetail(getString("pluginxdoc.mojodescriptor.parameter.since"), parameter.getSince(), w);
-            }
-
-            if (parameter.isRequired()) {
-                addedUl = addUl(w, addedUl);
-                writeDetail(getString("pluginxdoc.mojodescriptor.parameter.required"), getString("pluginxdoc.yes"), w);
-            } else {
-                addedUl = addUl(w, addedUl);
-                writeDetail(getString("pluginxdoc.mojodescriptor.parameter.required"), getString("pluginxdoc.no"), w);
-            }
-
-            String expression = parameter.getExpression();
-            addedUl = addUl(w, addedUl, expression);
-            String property = getPropertyFromExpression(expression);
-            if (property == null) {
-                writeDetail(getString("pluginxdoc.mojodescriptor.parameter.expression"), expression, w);
-            } else {
-                writeDetail(getString("pluginxdoc.mojodescriptor.parameter.property"), property, w);
-            }
-
-            addedUl = addUl(w, addedUl, parameter.getDefaultValue());
-            writeDetail(
-                    getString("pluginxdoc.mojodescriptor.parameter.default"),
-                    escapeXml(parameter.getDefaultValue()),
-                    w);
-
-            addedUl = addUl(w, addedUl, parameter.getAlias());
-            writeDetail(getString("pluginxdoc.mojodescriptor.parameter.alias"), escapeXml(parameter.getAlias()), w);
-
-            if (addedUl) {
-                w.endElement(); // ul
-            }
-
-            if (parameters.hasNext()) {
-                w.writeMarkup("<hr/>");
-            }
-        }
-
-        w.endElement();
-    }
-
-    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 String getLinkedType(Parameter parameter, boolean isShortType) {
-        final String typeValue;
-        if (isShortType) {
-            typeValue = getShortType(parameter.getType());
-        } else {
-            typeValue = parameter.getType();
-        }
-        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())) {
-                    return format(
-                            "pluginxdoc.mojodescriptor.parameter.type_link",
-                            new Object[] {escapeXml(typeValue), enhancedParameter.getTypeJavadocUrl()});
-                }
-            }
-        }
-        return escapeXml(typeValue);
-    }
-
-    private boolean addUl(XMLWriter w, boolean addedUl, String content) {
-        if (content != null && !content.isEmpty()) {
-            return addUl(w, addedUl);
-        }
-        return addedUl;
-    }
-
-    private boolean addUl(XMLWriter w, boolean addedUl) {
-        if (!addedUl) {
-            w.startElement("ul");
-            addedUl = true;
-        }
-        return addedUl;
-    }
-
-    private 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;
-    }
-
-    /**
-     * @param param not null
-     * @param value could be null
-     * @param w     not null
-     */
-    private void writeDetail(String param, String value, XMLWriter w) {
-        if (value != null && !value.isEmpty()) {
-            w.startElement("li");
-            w.writeMarkup(format("pluginxdoc.detail", new String[] {param, value}));
-            w.endElement(); // li
-        }
-    }
-
-    /**
-     * @param parameterList  not null
-     * @param w              not null
-     */
-    private void writeParameterSummary(List<Parameter> parameterList, XMLWriter w, String goal) {
-        List<Parameter> requiredParams = getParametersByRequired(true, parameterList);
-        if (!requiredParams.isEmpty()) {
-            writeParameterList(getString("pluginxdoc.mojodescriptor.requiredParameters"), requiredParams, w, goal);
-        }
-
-        List<Parameter> optionalParams = getParametersByRequired(false, parameterList);
-        if (!optionalParams.isEmpty()) {
-            writeParameterList(getString("pluginxdoc.mojodescriptor.optionalParameters"), optionalParams, w, goal);
-        }
-    }
-
-    /**
-     * @param title          not null
-     * @param parameterList  not null
-     * @param w              not null
-     */
-    private void writeParameterList(String title, List<Parameter> parameterList, XMLWriter w, String goal) {
-        w.startElement("subsection");
-        w.addAttribute("name", title);
-
-        w.startElement("table");
-        w.addAttribute("border", "0");
-        w.addAttribute("class", "bodyTable");
-
-        w.startElement("tr");
-        w.startElement("th");
-        w.writeText(getString("pluginxdoc.mojodescriptor.parameter.name"));
-        w.endElement(); // th
-        w.startElement("th");
-        w.writeText(getString("pluginxdoc.mojodescriptor.parameter.type"));
-        w.endElement(); // th
-        w.startElement("th");
-        w.writeText(getString("pluginxdoc.mojodescriptor.parameter.since"));
-        w.endElement(); // th
-        w.startElement("th");
-        w.writeText(getString("pluginxdoc.mojodescriptor.parameter.description"));
-        w.endElement(); // th
-        w.endElement(); // tr
-
-        for (Parameter parameter : parameterList) {
-            w.startElement("tr");
-
-            // name
-            w.startElement("td");
-            w.writeMarkup(format("pluginxdoc.mojodescriptor.parameter.name_link", parameter.getName()));
-            w.endElement(); // td
-
-            // type
-            w.startElement("td");
-            w.writeMarkup("<code>" + getLinkedType(parameter, true) + "</code>");
-            w.endElement(); // td
-
-            // since
-            w.startElement("td");
-            if (StringUtils.isNotEmpty(parameter.getSince())) {
-                w.writeMarkup("<code>" + parameter.getSince() + "</code>");
-            } else {
-                w.writeMarkup("<code>-</code>");
-            }
-            w.endElement(); // td
-
-            // description
-            w.startElement("td");
-            String description;
-            String context = "Parameter " + parameter.getName() + " in goal " + goal;
-            if (StringUtils.isNotEmpty(parameter.getDeprecated())) {
-                String deprecated = getXhtmlWithValidatedLinks(parameter.getDescription(), context);
-                description = format("pluginxdoc.mojodescriptor.parameter.deprecated", deprecated);
-            } else if (StringUtils.isNotEmpty(parameter.getDescription())) {
-                description = getXhtmlWithValidatedLinks(parameter.getDescription(), context);
-            } else {
-                description = getString("pluginxdoc.nodescription");
-            }
-            w.writeMarkup(description + "<br/>");
-
-            if (StringUtils.isNotEmpty(parameter.getDefaultValue())) {
-                w.writeMarkup(format(
-                        "pluginxdoc.mojodescriptor.parameter.defaultValue", escapeXml(parameter.getDefaultValue())));
-                w.writeMarkup("<br/>");
-            }
-
-            String property = getPropertyFromExpression(parameter.getExpression());
-            if (property != null) {
-                w.writeMarkup(format("pluginxdoc.mojodescriptor.parameter.property.description", property));
-                w.writeMarkup("<br/>");
-            }
-
-            if (StringUtils.isNotEmpty(parameter.getAlias())) {
-                w.writeMarkup(format(
-                        "pluginxdoc.mojodescriptor.parameter.alias.description", escapeXml(parameter.getAlias())));
-            }
-
-            w.endElement(); // td
-            w.endElement(); // tr
-        }
-
-        w.endElement(); // table
-        w.endElement(); // section
-    }
-
-    /**
-     * @param required      <code>true</code> for required parameters, <code>false</code> otherwise.
-     * @param parameterList not null
-     * @return list of parameters depending the value of <code>required</code>
-     */
-    private List<Parameter> getParametersByRequired(boolean required, List<Parameter> parameterList) {
-        List<Parameter> list = new ArrayList<>();
-
-        for (Parameter parameter : parameterList) {
-            if (parameter.isRequired() == required) {
-                list.add(parameter);
-            }
-        }
-
-        return list;
-    }
-
-    /**
-     * Gets the resource bundle for the <code>locale</code> instance variable.
-     *
-     * @return The resource bundle for the <code>locale</code> instance variable.
-     */
-    private ResourceBundle getBundle() {
-        return ResourceBundle.getBundle("pluginxdoc", locale, getClass().getClassLoader());
-    }
-
-    /**
-     * @param key not null
-     * @return Localized, text identified by <code>key</code>.
-     * @see #getBundle()
-     */
-    private String getString(String key) {
-        return getBundle().getString(key);
-    }
-
-    /**
-     * Convenience method.
-     *
-     * @param key  not null
-     * @param arg1 not null
-     * @return Localized, formatted text identified by <code>key</code>.
-     * @see #format(String, Object[])
-     */
-    private String format(String key, Object arg1) {
-        return format(key, new Object[] {arg1});
-    }
-
-    /**
-     * Looks up the value for <code>key</code> in the <code>ResourceBundle</code>,
-     * then formats that value for the specified <code>Locale</code> using <code>args</code>.
-     *
-     * @param key  not null
-     * @param args not null
-     * @return Localized, formatted text identified by <code>key</code>.
-     */
-    private String format(String key, Object[] args) {
-        String pattern = getString(key);
-        // we don't need quoting so spare us the confusion in the resource bundle to double them up in some keys
-        pattern = StringUtils.replace(pattern, "'", "''");
-
-        MessageFormat messageFormat = new MessageFormat("");
-        messageFormat.setLocale(locale);
-        messageFormat.applyPattern(pattern);
-
-        return messageFormat.format(args);
-    }
-
-    /**
-     * @param text the string to escape
-     * @return A string escaped with XML entities
-     */
-    private String escapeXml(String text) {
-        if (text != null) {
-            text = text.replace("&", "&amp;");
-            text = text.replace("<", "&lt;");
-            text = text.replace(">", "&gt;");
-            text = text.replace("\"", "&quot;");
-            text = text.replace("\'", "&apos;");
-        }
-        return text;
-    }
-
-    String getXhtmlWithValidatedLinks(String xhtmlText, String context) {
-        if (disableInternalJavadocLinkValidation) {
-            return xhtmlText;
-        }
-        StringBuffer sanitizedXhtmlText = new StringBuffer();
-        // find all links which are not absolute
-        Matcher matcher = HTML_LINK_PATTERN.matcher(xhtmlText);
-        while (matcher.find()) {
-            URI link;
-            try {
-                link = new URI(matcher.group(1));
-                if (!link.isAbsolute() && !JavadocLinkGenerator.isLinkValid(link, reportOutputDirectory.toPath())) {
-                    matcher.appendReplacement(sanitizedXhtmlText, matcher.group(2));
-                    LOG.debug("Removed invalid link {} in {}", link, context);
-                } else {
-                    matcher.appendReplacement(sanitizedXhtmlText, matcher.group(0));
-                }
-            } catch (URISyntaxException e) {
-                LOG.warn("Invalid URI {} found in {}. Cannot validate, leave untouched", matcher.group(1), context);
-                matcher.appendReplacement(sanitizedXhtmlText, matcher.group(0));
-            }
-        }
-        matcher.appendTail(sanitizedXhtmlText);
-        return sanitizedXhtmlText.toString();
-    }
-}
diff --git a/maven-plugin-tools-generators/src/main/resources/pluginxdoc.properties b/maven-plugin-tools-generators/src/main/resources/pluginxdoc.properties
deleted file mode 100644
index 2bda11ab..00000000
--- a/maven-plugin-tools-generators/src/main/resources/pluginxdoc.properties
+++ /dev/null
@@ -1,64 +0,0 @@
-# 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.
-
-pluginxdoc.yes=Yes
-pluginxdoc.no=No
-pluginxdoc.detail=<strong>{0}</strong>: <code>{1}</code>
-
-pluginxdoc.description=<strong>Description</strong>:
-pluginxdoc.nodescription=(no description)
-pluginxdoc.mojodescriptor.notice.note=<strong>Note</strong>:
-pluginxdoc.mojodescriptor.notice.isMavenReport=This goal should be used as a Maven report.
-pluginxdoc.mojodescriptor.fullname=<strong>Full name</strong>:
-pluginxdoc.mojodescriptor.attributes=<strong>Attributes</strong>:
-pluginxdoc.mojodescriptor.deprecated=<strong>This plugin goal has been deprecated</strong>:
-pluginxdoc.mojodescriptor.projectRequired=Requires a Maven project to be executed.
-pluginxdoc.mojodescriptor.aggregator=Executes as an aggregator goal.
-pluginxdoc.mojodescriptor.directInvocationOnly=Executes by direct invocation only.
-pluginxdoc.mojodescriptor.reportingMojo=Executes only as a reportSet (reporting goal).
-pluginxdoc.mojodescriptor.dependencyResolutionRequired=Requires dependency resolution of artifacts in scope: <code>{0}</code>.
-pluginxdoc.mojodescriptor.dependencyCollectionRequired=Requires dependency collection of artifacts in scope: <code>{0}</code>.
-pluginxdoc.mojodescriptor.since=Since version: <code>{0}</code>
-pluginxdoc.mojodescriptor.phase=Binds by default to the <a href="http://maven.apache.org/ref/current/maven-core/lifecycles.html">lifecycle phase</a>: <code>{0}</code>.
-pluginxdoc.mojodescriptor.executePhase=Invokes the execution of the <a href="http://maven.apache.org/ref/current/maven-core/lifecycles.html">lifecycle phase</a> <code>{0}</code> prior to executing itself.
-pluginxdoc.mojodescriptor.executeGoal=Invokes the execution of this plugin's goal <code>{0}</code> prior to executing itself.
-pluginxdoc.mojodescriptor.executeLifecycle=Executes in its own lifecycle: <code>{0}</code>.
-pluginxdoc.mojodescriptor.onlineRequired=Requires that Maven runs in online mode.
-pluginxdoc.mojodescriptor.inheritedByDefault=Is NOT inherited by default in multi-project builds.
-pluginxdoc.mojodescriptor.parameter.details=Parameter Details
-pluginxdoc.mojodescriptor.parameter.deprecated=<strong>Deprecated.</strong> {0}
-pluginxdoc.mojodescriptor.parameter.name=Name
-pluginxdoc.mojodescriptor.parameter.name_link=<code><a href="#{0}">&lt;{0}&gt;</a></code>
-pluginxdoc.mojodescriptor.parameter.name_internal=<strong><a name="{0}">&lt;{0}&gt;</a></strong>
-pluginxdoc.mojodescriptor.parameter.description=Description
-pluginxdoc.mojodescriptor.parameter.type=Type
-pluginxdoc.mojodescriptor.parameter.type_link=<a href="{1}">{0}</a>
-pluginxdoc.mojodescriptor.parameter.since=Since
-pluginxdoc.mojodescriptor.parameter.required=Required
-pluginxdoc.mojodescriptor.parameter.expression=Expression
-pluginxdoc.mojodescriptor.parameter.property=User Property
-pluginxdoc.mojodescriptor.parameter.property.description=<strong>User property</strong>: <code>{0}</code>
-pluginxdoc.mojodescriptor.parameter.default=Default
-pluginxdoc.mojodescriptor.parameter.defaultValue=<strong>Default value</strong>: <code>{0}</code>
-pluginxdoc.mojodescriptor.parameter.alias=Alias
-pluginxdoc.mojodescriptor.parameter.alias.description=<strong>Alias</strong>: <code>{0}</code>
-pluginxdoc.mojodescriptor.requiredParameters=Required Parameters
-pluginxdoc.mojodescriptor.optionalParameters=Optional Parameters
-pluginxdoc.mojodescriptor.parameters=Parameters
-pluginxdoc.mojodescriptor.noParameter=(no parameters)
-pluginxdoc.mojodescriptor.threadSafe=The goal is thread-safe and supports parallel builds.
-pluginxdoc.mojodescriptor.notThreadSafe=The goal is not marked as thread-safe and thus does not support parallel builds.
diff --git a/maven-plugin-tools-generators/src/main/resources/pluginxdoc_de.properties b/maven-plugin-tools-generators/src/main/resources/pluginxdoc_de.properties
deleted file mode 100644
index c326151e..00000000
--- a/maven-plugin-tools-generators/src/main/resources/pluginxdoc_de.properties
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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.
-
-pluginxdoc.yes=Ja
-pluginxdoc.no=Nein
-
-pluginxdoc.description=<strong>Beschreibung</strong>:
-pluginxdoc.nodescription=(keine Beschreibung)
-pluginxdoc.mojodescriptor.notice.note=<strong>Anmerkung</strong>:
-pluginxdoc.mojodescriptor.notice.isMavenReport=Dieses Goal erzeugt einen Maven-Bericht.
-pluginxdoc.mojodescriptor.fullname=<strong>Vollst\u00e4ndiger Name</strong>:
-pluginxdoc.mojodescriptor.attributes=<strong>Attribute</strong>:
-pluginxdoc.mojodescriptor.deprecated=<strong>Dieses Plugin-Goal ist missbilligt</strong>:
-pluginxdoc.mojodescriptor.projectRequired=Erfordert ein Maven Projekt zur Ausf\u00fchrung.
-pluginxdoc.mojodescriptor.aggregator=Wird als Aggregator ausgef\u00fchrt.
-pluginxdoc.mojodescriptor.directInvocationOnly=Wird nur per direktem Aufruf ausgef\u00fchrt.
-pluginxdoc.mojodescriptor.dependencyResolutionRequired=Erfordert Abh\u00e4ngigkeitsaufl\u00f6sung von Artefakten mit Geltungsbereich: <code>{0}</code>.
-pluginxdoc.mojodescriptor.dependencyCollectionRequired=Erfordert Abh\u00e4ngigkeitsermittlung von Artefakten mit Geltungsbereich: <code>{0}</code>.
-pluginxdoc.mojodescriptor.since=Seit Version: <code>{0}</code>.
-pluginxdoc.mojodescriptor.phase=Bindet automatisch an die <a href="http://maven.apache.org/ref/current/maven-core/lifecycles.html">Lebenszyklusphase</a>: <code>{0}</code>.
-pluginxdoc.mojodescriptor.executePhase=Bedingt die Ausf\u00fchrung der <a href="http://maven.apache.org/ref/current/maven-core/lifecycles.html">Lebenszyklusphase</a> <code>{0}</code> vor seiner eigenen Ausf\u00fchrung.
-pluginxdoc.mojodescriptor.executeGoal=Bedingt die Ausf\u00fchrung des Plugin-Goals <code>{0}</code> vor seiner eigenen Ausf\u00fchrung.
-pluginxdoc.mojodescriptor.executeLifecycle=Wird in seinem eigenen Lebenszyklus ausgef\u00fchrt: <code>{0}</code>.
-pluginxdoc.mojodescriptor.onlineRequired=Erfordert die Ausf\u00fchrung von Maven im Online-Modus.
-pluginxdoc.mojodescriptor.inheritedByDefault=Wird bei einem Multi-Projekt-Build standardm\u00e4\u00dfig <em>nicht</em> vererbt.
-pluginxdoc.mojodescriptor.parameter.details=Parameter-Details
-pluginxdoc.mojodescriptor.parameter.deprecated=<strong>Missbilligt.</strong> {0}
-pluginxdoc.mojodescriptor.parameter.name=Name
-pluginxdoc.mojodescriptor.parameter.description=Beschreibung
-pluginxdoc.mojodescriptor.parameter.type=Typ
-pluginxdoc.mojodescriptor.parameter.since=Seit
-pluginxdoc.mojodescriptor.parameter.required=Erforderlich
-pluginxdoc.mojodescriptor.parameter.expression=Ausdruck
-pluginxdoc.mojodescriptor.parameter.default=Standard
-pluginxdoc.mojodescriptor.parameter.defaultValue=<strong>Standardwert ist</strong>: <code>{0}</code>.
-pluginxdoc.mojodescriptor.parameter.alias=Alias
-pluginxdoc.mojodescriptor.parameter.alias.description=<strong>Alias ist</strong>: <code>{0}</code>.
-pluginxdoc.mojodescriptor.requiredParameters=Erforderliche Parameter
-pluginxdoc.mojodescriptor.optionalParameters=Optionale Parameter
-pluginxdoc.mojodescriptor.parameters=Parameter
-pluginxdoc.mojodescriptor.noParameter=(keine Parameter)
-pluginxdoc.mojodescriptor.threadSafe=Dieses Goal is thread-sicher und unterst\u00fctzt paralleles Bauen.
diff --git a/maven-plugin-tools-generators/src/main/resources/pluginxdoc_en.properties b/maven-plugin-tools-generators/src/main/resources/pluginxdoc_en.properties
deleted file mode 100644
index 96cf4075..00000000
--- a/maven-plugin-tools-generators/src/main/resources/pluginxdoc_en.properties
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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.
-
-# NOTE:
-# This bundle is intentionally empty because English strings are provided by the base bundle via the parent chain. It
-# must be provided nevertheless such that a request for locale "en" will not errorneously pick up the bundle for the
-# JVM's default locale (which need not be "en"). See the method javadoc about
-#   ResourceBundle.getBundle(String, Locale, ClassLoader)
-# for a full description of the lookup strategy.
diff --git a/maven-plugin-tools-generators/src/main/resources/pluginxdoc_fr.properties b/maven-plugin-tools-generators/src/main/resources/pluginxdoc_fr.properties
deleted file mode 100644
index a687851d..00000000
--- a/maven-plugin-tools-generators/src/main/resources/pluginxdoc_fr.properties
+++ /dev/null
@@ -1,60 +0,0 @@
-# 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.
-
-pluginxdoc.yes=Oui
-pluginxdoc.no=Non
-pluginxdoc.detail=<strong>{0}</strong> : <code>{1}</code>
-
-pluginxdoc.description=<strong>Description</strong> :
-pluginxdoc.nodescription=(aucune description)
-pluginxdoc.mojodescriptor.notice.note=<strong>Note</strong> :
-pluginxdoc.mojodescriptor.notice.isMavenReport=Ce goal doit \u00eatre utilis\u00e9 comme un rapport de Maven.
-pluginxdoc.mojodescriptor.fullname=<strong>Nom complet</strong> :
-pluginxdoc.mojodescriptor.attributes=<strong>Attributs</strong> :
-pluginxdoc.mojodescriptor.deprecated=<strong>Le goal de ce plugin est obsol\u00e8te</strong> :
-pluginxdoc.mojodescriptor.projectRequired=Exige un projet Maven pour \u00eatre ex\u00e9cut\u00e9.
-pluginxdoc.mojodescriptor.aggregator=S'ex\u00e9cute comme un goal agr\u00e9g\u00e9.
-pluginxdoc.mojodescriptor.directInvocationOnly=S'ex\u00e9cute par l'invocation directe seulement.
-pluginxdoc.mojodescriptor.reportingMojo=S'ex\u00e9cute seulement comme un reportSet (goal de reporting).
-pluginxdoc.mojodescriptor.dependencyResolutionRequired=Exige une r\u00e9solution des d\u00e9pendances des artefacts dans le scope : <code>{0}</code>.
-pluginxdoc.mojodescriptor.dependencyCollectionRequired=Exige une collecte des d\u00e9pendances des artefacts dans le scope : <code>{0}</code>.
-pluginxdoc.mojodescriptor.since=Depuis la version : <code>{0}</code>.
-pluginxdoc.mojodescriptor.phase=S'associe par d\u00e9faut dans la <a href="http://maven.apache.org/ref/current/maven-core/lifecycles.html">phase du cycle de vie</a> : <code>{0}</code>.
-pluginxdoc.mojodescriptor.executePhase=Appelle l'ex\u00e9cution de la <a href="http://maven.apache.org/ref/current/maven-core/lifecycles.html">phase du cycle de vie</a> <code>{0}</code> avant de s'ex\u00e9cuter.
-pluginxdoc.mojodescriptor.executeGoal=Appelle l'ex\u00e9cution du goal de ce plugin <code>{0}</code> avant de s'ex\u00e9cuter.
-pluginxdoc.mojodescriptor.executeLifecycle=S'ex\u00e9cute dans son propre cycle de vie : <code>{0}</code>.
-pluginxdoc.mojodescriptor.onlineRequired=Exige que Maven s'ex\u00e9cute en ligne.
-pluginxdoc.mojodescriptor.inheritedByDefault=N'est PAS h\u00e9rit\u00e9 par d\u00e9faut dans une construction multi-projet.
-pluginxdoc.mojodescriptor.parameter.details=D\u00e9tails des param\u00e8tres
-pluginxdoc.mojodescriptor.parameter.deprecated=<strong>Obsol\u00e8te.</strong> {0}
-pluginxdoc.mojodescriptor.parameter.name=Nom
-pluginxdoc.mojodescriptor.parameter.description=Description
-pluginxdoc.mojodescriptor.parameter.type=Type
-pluginxdoc.mojodescriptor.parameter.since=Depuis
-pluginxdoc.mojodescriptor.parameter.required=Exig\u00e9
-pluginxdoc.mojodescriptor.parameter.expression=Expression
-pluginxdoc.mojodescriptor.parameter.property=Propri\u00e9t\u00e9 utilisateur
-pluginxdoc.mojodescriptor.parameter.property.description=<strong>Propri\u00e9t\u00e9 utilisateur</strong> : <code>{0}</code>.
-pluginxdoc.mojodescriptor.parameter.default=D\u00e9faut
-pluginxdoc.mojodescriptor.parameter.defaultValue=<strong>Valeur par d\u00e9faut</strong> : <code>{0}</code>.
-pluginxdoc.mojodescriptor.parameter.alias=Alias
-pluginxdoc.mojodescriptor.parameter.alias.description=<strong>Alias</strong> : <code>{0}</code>.
-pluginxdoc.mojodescriptor.requiredParameters=Param\u00e8tres requis
-pluginxdoc.mojodescriptor.optionalParameters=Param\u00e8tres optionnels
-pluginxdoc.mojodescriptor.parameters=Param\u00e8tres
-pluginxdoc.mojodescriptor.noParameter=(aucun param\u00e8tre)
-pluginxdoc.mojodescriptor.threadSafe=Le goal est thread-safe et supporte des constructions parall\u00e8les.
diff --git a/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/GeneratorUtilsTest.java b/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/GeneratorUtilsTest.java
index 86aa0594..d0e10c8e 100644
--- a/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/GeneratorUtilsTest.java
+++ b/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/GeneratorUtilsTest.java
@@ -26,15 +26,12 @@ import java.util.List;
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
-import org.apache.maven.plugin.testing.stubs.MavenProjectStub;
 import org.codehaus.plexus.component.repository.ComponentDependency;
 import org.codehaus.plexus.util.xml.CompactXMLWriter;
 import org.codehaus.plexus.util.xml.XMLWriter;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * @author jdcasey
@@ -185,25 +182,6 @@ class GeneratorUtilsTest {
         // assertEquals( "Generates something for the project.", GeneratorUtils.toText( javadoc ) );
     }
 
-    @Test
-    void testIsMavenReport() throws Exception {
-        try {
-            GeneratorUtils.isMavenReport(null, null);
-        } catch (IllegalArgumentException e) {
-            assertTrue(true);
-        }
-
-        String impl = "org.apache.maven.tools.plugin.generator.stubs.MavenReportStub";
-
-        MavenProjectStub stub = new MavenProjectStub();
-        stub.setCompileSourceRoots(Collections.singletonList(System.getProperty("basedir") + "/target/classes"));
-
-        assertTrue(GeneratorUtils.isMavenReport(impl, stub));
-
-        impl = "org.apache.maven.tools.plugin.util.stubs.MojoStub";
-        assertFalse(GeneratorUtils.isMavenReport(impl, stub));
-    }
-
     @Test
     void testExcludeProvidedScopeFormComponentDependencies() {
 
diff --git a/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/PluginXdocGeneratorTest.java b/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/PluginXdocGeneratorTest.java
deleted file mode 100644
index 5d6cd695..00000000
--- a/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/PluginXdocGeneratorTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.tools.plugin.generator;
-
-import java.io.File;
-import java.io.InputStream;
-import java.util.Locale;
-
-import org.codehaus.plexus.util.ReaderFactory;
-import org.codehaus.plexus.util.xml.Xpp3Dom;
-import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * @author <a href="mailto:jason@maven.org">Jason van Zyl </a>
- * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
- */
-public class PluginXdocGeneratorTest extends AbstractGeneratorTestCase {
-
-    // inherits tests from base class
-
-    @Override
-    protected void validate(File destinationDirectory) throws Exception {
-        File docFile = new File(destinationDirectory, "testGoal-mojo.xml");
-        Xpp3Dom actual = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(docFile));
-
-        InputStream expectedAsStream = getClass().getResourceAsStream("/expected-testGoal-mojo.xml");
-
-        Xpp3Dom expected = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(expectedAsStream));
-
-        Assertions.assertEquals(expected, actual);
-    }
-
-    @Test
-    void testGetShortType() {
-        assertEquals("String", PluginXdocGenerator.getShortType("java.lang.String"));
-        assertEquals("List<String>", PluginXdocGenerator.getShortType("java.util.List<java.lang.String>"));
-        assertEquals(
-                "Map<String,Integer>",
-                PluginXdocGenerator.getShortType("java.util.Map<java.lang.String,java.lang.Integer>"));
-        assertEquals("List<...>", PluginXdocGenerator.getShortType("java.util.List<java.util.List<java.lang.String>>"));
-    }
-
-    @Test
-    void testGetXhtmlWithValidatedLinks() {
-        File baseDir = new File(this.getClass().getResource("").getFile());
-        PluginXdocGenerator xdocGenerator = new PluginXdocGenerator(null, Locale.ROOT, baseDir, false);
-        PluginXdocGenerator xdocGeneratorWithDisabledLinkValidator =
-                new PluginXdocGenerator(null, Locale.ROOT, baseDir, true);
-        String externalLink =
-                "test<a href=\"http://example.com/test\">External Link</a>..and a second<a href=\"http://localhost/example\">link</a>end";
-        assertEquals(externalLink, xdocGenerator.getXhtmlWithValidatedLinks(externalLink, "test"));
-        assertEquals(
-                externalLink, xdocGeneratorWithDisabledLinkValidator.getXhtmlWithValidatedLinks(externalLink, "test"));
-        String validInternalLink =
-                "test<a href=\"PluginXdocGeneratorTest.class\">Internal Link</a>..and a second<a href=\"http://localhost/example\">link</a>end";
-        assertEquals(validInternalLink, xdocGenerator.getXhtmlWithValidatedLinks(validInternalLink, "test"));
-        assertEquals(
-                validInternalLink,
-                xdocGeneratorWithDisabledLinkValidator.getXhtmlWithValidatedLinks(validInternalLink, "test"));
-        String invalidInternalLink =
-                "test<a href=\"PluginXdocGeneratorTestinvalid.class\">Internal Link</a>..and a second<a href=\"http://localhost/example\">link</a>end";
-        String sanitizedInvalidInternalLink =
-                "testInternal Link..and a second<a href=\"http://localhost/example\">link</a>end";
-        assertEquals(
-                sanitizedInvalidInternalLink, xdocGenerator.getXhtmlWithValidatedLinks(invalidInternalLink, "test"));
-        assertEquals(
-                invalidInternalLink,
-                xdocGeneratorWithDisabledLinkValidator.getXhtmlWithValidatedLinks(invalidInternalLink, "test"));
-    }
-}