This is an automated email from the ASF dual-hosted git repository. kwin pushed a commit to branch feature/improve-javadoc-link-validation in repository https://gitbox.apache.org/repos/asf/maven-plugin-tools.git
commit d10e78fb3d48aa6de7b44b64e193ef39dffb5e37 Author: Konrad Windszus <[email protected]> AuthorDate: Wed Oct 26 17:36:39 2022 +0200 [MPLUGIN-433] Allow to disable link validation Also validate internal links to javadocs in mojo/parameter description and deprecated --- .../plugin/plugin/report_old/PluginReport.java | 9 ++- .../maven/plugin/plugin/report/PluginReport.java | 16 +++- .../plugin/generator/PluginXdocGenerator.java | 87 ++++++++++++++++++---- .../plugin/generator/PluginXdocGeneratorTest.java | 19 +++++ 4 files changed, 110 insertions(+), 21 deletions(-) diff --git a/maven-plugin-plugin/src/main/java/org/apache/maven/plugin/plugin/report_old/PluginReport.java b/maven-plugin-plugin/src/main/java/org/apache/maven/plugin/plugin/report_old/PluginReport.java index de607f09..d3bd6182 100644 --- a/maven-plugin-plugin/src/main/java/org/apache/maven/plugin/plugin/report_old/PluginReport.java +++ b/maven-plugin-plugin/src/main/java/org/apache/maven/plugin/plugin/report_old/PluginReport.java @@ -219,10 +219,11 @@ public class PluginReport private File enhancedPluginXmlFile; /** - * In case the internal javadoc site has not been generated (e.g. when using an aggregator javadoc report) - * link validation needs to be disabled by setting this value to {@code true}. - * This might have the drawback that some links being generated in the report are broken in case not all - * parameter types and javadoc link references are resolvable through the sites being given to + * In case the internal javadoc site has not been generated when running this report goal + * (e.g. when using an aggregator javadoc report) link validation needs to be disabled by setting + * this value to {@code true}. + * This might have the drawback that some links being generated in the report might be broken + * in case not all parameter types and javadoc link references are resolvable through the sites being given to * {@link DescriptorGeneratorMojo}. * * @since 3.7.0 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 c865899c..1fc55777 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 @@ -215,6 +215,19 @@ public class PluginReport readonly = true ) private File enhancedPluginXmlFile; + /** + * In case the internal javadoc site has not been generated when running this report goal + * (e.g. when using an aggregator javadoc report) link validation needs to be disabled by setting + * this value to {@code true}. + * This might have the drawback that some links being generated in the report might be broken + * in case not all parameter types and javadoc link references are resolvable through the sites being given to + * goal {@code plugin:descriptor}. + * + * @since 3.7.0 + */ + @Parameter + private boolean disableInternalJavadocLinkValidation; + /** * {@inheritDoc} */ @@ -317,7 +330,8 @@ public class PluginReport File outputDir = outputDirectory; outputDir.mkdirs(); - PluginXdocGenerator generator = new PluginXdocGenerator( getProject(), locale, getReportOutputDirectory() ); + PluginXdocGenerator generator = new PluginXdocGenerator( getProject(), locale, getReportOutputDirectory(), + disableInternalJavadocLinkValidation ); PluginToolsRequest pluginToolsRequest = new DefaultPluginToolsRequest( getProject(), pluginDescriptor ); generator.execute( outputDir, pluginToolsRequest ); } 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 index 2694ad08..7ca8a377 100644 --- 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 @@ -25,12 +25,15 @@ 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; @@ -43,6 +46,8 @@ 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; @@ -52,6 +57,14 @@ import static java.nio.charset.StandardCharsets.UTF_8; 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 */ @@ -93,7 +106,8 @@ public class PluginXdocGenerator * @param project not null. * @param locale not null wanted locale. */ - public PluginXdocGenerator( MavenProject project, Locale locale, File reportOutputDirectory, boolean disableInternalJavadocLinkValidation ) + public PluginXdocGenerator( MavenProject project, Locale locale, File reportOutputDirectory, + boolean disableInternalJavadocLinkValidation ) { this.project = project; if ( locale == null ) @@ -207,13 +221,14 @@ public class PluginXdocGenerator + mojoDescriptor.getPluginDescriptor().getVersion() + ":" + 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( mojoDescriptor.getDeprecated() ); + w.writeMarkup( getXhtmlWithValidatedLinks( mojoDescriptor.getDeprecated(), context ) ); w.endElement(); // div } @@ -223,7 +238,7 @@ public class PluginXdocGenerator w.startElement( "div" ); if ( StringUtils.isNotEmpty( mojoDescriptor.getDescription() ) ) { - w.writeMarkup( mojoDescriptor.getDescription() ); + w.writeMarkup( getXhtmlWithValidatedLinks( mojoDescriptor.getDescription(), context ) ); } else { @@ -411,8 +426,8 @@ public class PluginXdocGenerator if ( !list.isEmpty() ) { - writeParameterSummary( list, w ); - writeParameterDetails( list, w ); + writeParameterSummary( list, w, mojoDescriptor.getGoal() ); + writeParameterDetails( list, w, mojoDescriptor.getGoal() ); } else { @@ -460,7 +475,7 @@ public class PluginXdocGenerator * @param parameterList not null * @param w not null */ - private void writeParameterDetails( List<Parameter> parameterList, XMLWriter w ) + private void writeParameterDetails( List<Parameter> parameterList, XMLWriter w, String goal ) { w.startElement( "subsection" ); w.addAttribute( "name", getString( "pluginxdoc.mojodescriptor.parameter.details" ) ); @@ -473,17 +488,20 @@ public class PluginXdocGenerator 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" ); - w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.deprecated", parameter.getDeprecated() ) ); + 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( parameter.getDescription() ); + + w.writeMarkup( getXhtmlWithValidatedLinks( parameter.getDescription(), context ) ); } else { @@ -616,10 +634,11 @@ public class PluginXdocGenerator EnhancedParameterWrapper enhancedParameter = (EnhancedParameterWrapper) parameter; if ( enhancedParameter.getTypeJavadocUrl() != null ) { - // check if link is valid URI javadocUrl = enhancedParameter.getTypeJavadocUrl(); + // optionally check if link is valid if ( javadocUrl.isAbsolute() - || !disableInternalJavadocLinkValidation && JavadocLinkGenerator.isLinkValid( javadocUrl, reportOutputDirectory.toPath() ) ) + || !disableInternalJavadocLinkValidation + && JavadocLinkGenerator.isLinkValid( javadocUrl, reportOutputDirectory.toPath() ) ) { return format( "pluginxdoc.mojodescriptor.parameter.type_link", new Object[] { escapeXml( typeValue ), enhancedParameter.getTypeJavadocUrl() } ); @@ -679,20 +698,20 @@ public class PluginXdocGenerator * @param parameterList not null * @param w not null */ - private void writeParameterSummary( List<Parameter> parameterList, XMLWriter w ) + 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 ); + requiredParams, w, goal ); } List<Parameter> optionalParams = getParametersByRequired( false, parameterList ); if ( !optionalParams.isEmpty() ) { writeParameterList( getString( "pluginxdoc.mojodescriptor.optionalParameters" ), - optionalParams, w ); + optionalParams, w, goal ); } } @@ -701,7 +720,7 @@ public class PluginXdocGenerator * @param parameterList not null * @param w not null */ - private void writeParameterList( String title, List<Parameter> parameterList, XMLWriter w ) + private void writeParameterList( String title, List<Parameter> parameterList, XMLWriter w, String goal ) { w.startElement( "subsection" ); w.addAttribute( "name", title ); @@ -753,13 +772,15 @@ public class PluginXdocGenerator // description w.startElement( "td" ); String description; + String context = "Parameter " + parameter.getName() + " in goal " + goal; if ( StringUtils.isNotEmpty( parameter.getDeprecated() ) ) { - description = format( "pluginxdoc.mojodescriptor.parameter.deprecated", parameter.getDeprecated() ); + String deprecated = getXhtmlWithValidatedLinks( parameter.getDescription(), context ); + description = format( "pluginxdoc.mojodescriptor.parameter.deprecated", deprecated ); } else if ( StringUtils.isNotEmpty( parameter.getDescription() ) ) { - description = parameter.getDescription(); + description = getXhtmlWithValidatedLinks( parameter.getDescription(), context ); } else { @@ -886,4 +907,38 @@ public class PluginXdocGenerator 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/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 index 05903058..9cab7af3 100644 --- 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 @@ -21,6 +21,7 @@ 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; @@ -63,4 +64,22 @@ public class PluginXdocGeneratorTest 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" ) ); + } }
