From a0a8dad15e48d472c927949ee34a6eb199dba948 Mon Sep 17 00:00:00 2001 From: Konrad Windszus Date: Wed, 26 Oct 2022 14:31:33 +0200 Subject: [PATCH] [MPLUGIN-433] Allow to disable link validation Also validate internal links to javadocs in mojo/parameter description and deprecated info --- .../plugin/report_old/PluginReport.java | 16 +++- .../plugin/plugin/report/PluginReport.java | 16 +++- .../plugin/javadoc/JavadocLinkGenerator.java | 8 +- maven-plugin-tools-generators/pom.xml | 5 + .../plugin/generator/PluginXdocGenerator.java | 92 +++++++++++++++---- .../generator/PluginXdocGeneratorTest.java | 19 ++++ 6 files changed, 136 insertions(+), 20 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 bc34746d0..d3bd6182d 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 @@ -218,6 +218,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 + * {@link DescriptorGeneratorMojo}. + * + * @since 3.7.0 + */ + @Parameter + private boolean disableInternalJavadocLinkValidation; + /** * {@inheritDoc} */ @@ -324,7 +337,8 @@ private void generateMojosDocumentation( PluginDescriptor pluginDescriptor, Loca 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-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 63da351a8..4e86d4cf1 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 @@ -130,6 +130,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} */ @@ -232,7 +245,8 @@ private void generateMojosDocumentation( PluginDescriptor pluginDescriptor, Loca 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-api/src/main/java/org/apache/maven/tools/plugin/javadoc/JavadocLinkGenerator.java b/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/javadoc/JavadocLinkGenerator.java index fa4cedef6..a1cb9afa3 100644 --- a/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/javadoc/JavadocLinkGenerator.java +++ b/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/javadoc/JavadocLinkGenerator.java @@ -253,7 +253,13 @@ public static boolean isLinkValid( URI url, Path baseDirectory ) } else { - return Files.exists( baseDirectory.resolve( url.getPath() ) ); + Path file = baseDirectory.resolve( url.getPath() ); + boolean exists = Files.exists( file ); + if ( !exists ) + { + LOG.debug( "Could not find file given through '{}' in resolved path '{}'", url, file ); + } + return exists; } } } diff --git a/maven-plugin-tools-generators/pom.xml b/maven-plugin-tools-generators/pom.xml index 3fb4b4446..3ad4365f7 100644 --- a/maven-plugin-tools-generators/pom.xml +++ b/maven-plugin-tools-generators/pom.xml @@ -113,6 +113,11 @@ maven-plugin-testing-harness test + + org.slf4j + slf4j-simple + test + 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 c13a17908..7ca8a377e 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.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.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 @@ 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( "(.*?)" ); + + private static final Logger LOG = LoggerFactory.getLogger( PluginXdocGenerator.class ); + /** * locale */ @@ -68,6 +81,8 @@ public class PluginXdocGenerator */ private final File reportOutputDirectory; + private final boolean disableInternalJavadocLinkValidation; + /** * Default constructor using Locale.ENGLISH as locale. * Used only in test cases. @@ -84,14 +99,15 @@ public PluginXdocGenerator() */ public PluginXdocGenerator( MavenProject project ) { - this( project, Locale.ENGLISH, new File( "" ).getAbsoluteFile() ); + this( project, Locale.ENGLISH, new File( "" ).getAbsoluteFile(), false ); } /** * @param project not null. * @param locale not null wanted locale. */ - public PluginXdocGenerator( MavenProject project, Locale locale, File reportOutputDirectory ) + public PluginXdocGenerator( MavenProject project, Locale locale, File reportOutputDirectory, + boolean disableInternalJavadocLinkValidation ) { this.project = project; if ( locale == null ) @@ -103,6 +119,7 @@ public PluginXdocGenerator( MavenProject project, Locale locale, File reportOutp this.locale = locale; } this.reportOutputDirectory = reportOutputDirectory; + this.disableInternalJavadocLinkValidation = disableInternalJavadocLinkValidation; } @@ -204,13 +221,14 @@ private void writeBody( MojoDescriptor mojoDescriptor, XMLWriter w ) + 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 } @@ -220,7 +238,7 @@ private void writeBody( MojoDescriptor mojoDescriptor, XMLWriter w ) w.startElement( "div" ); if ( StringUtils.isNotEmpty( mojoDescriptor.getDescription() ) ) { - w.writeMarkup( mojoDescriptor.getDescription() ); + w.writeMarkup( getXhtmlWithValidatedLinks( mojoDescriptor.getDescription(), context ) ); } else { @@ -408,8 +426,8 @@ private void writeGoalParameterTable( MojoDescriptor mojoDescriptor, XMLWriter w if ( !list.isEmpty() ) { - writeParameterSummary( list, w ); - writeParameterDetails( list, w ); + writeParameterSummary( list, w, mojoDescriptor.getGoal() ); + writeParameterDetails( list, w, mojoDescriptor.getGoal() ); } else { @@ -457,7 +475,7 @@ private List filterParameters( List parameterList ) * @param parameterList not null * @param w not null */ - private void writeParameterDetails( List parameterList, XMLWriter w ) + private void writeParameterDetails( List parameterList, XMLWriter w, String goal ) { w.startElement( "subsection" ); w.addAttribute( "name", getString( "pluginxdoc.mojodescriptor.parameter.details" ) ); @@ -470,17 +488,20 @@ private void writeParameterDetails( List parameterList, XMLWriter w ) 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 { @@ -613,10 +634,11 @@ private String getLinkedType( Parameter parameter, boolean isShortType ) 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() - || JavadocLinkGenerator.isLinkValid( javadocUrl, reportOutputDirectory.toPath() ) ) + || !disableInternalJavadocLinkValidation + && JavadocLinkGenerator.isLinkValid( javadocUrl, reportOutputDirectory.toPath() ) ) { return format( "pluginxdoc.mojodescriptor.parameter.type_link", new Object[] { escapeXml( typeValue ), enhancedParameter.getTypeJavadocUrl() } ); @@ -676,20 +698,20 @@ private void writeDetail( String param, String value, XMLWriter w ) * @param parameterList not null * @param w not null */ - private void writeParameterSummary( List parameterList, XMLWriter w ) + private void writeParameterSummary( List parameterList, XMLWriter w, String goal ) { List requiredParams = getParametersByRequired( true, parameterList ); if ( !requiredParams.isEmpty() ) { writeParameterList( getString( "pluginxdoc.mojodescriptor.requiredParameters" ), - requiredParams, w ); + requiredParams, w, goal ); } List optionalParams = getParametersByRequired( false, parameterList ); if ( !optionalParams.isEmpty() ) { writeParameterList( getString( "pluginxdoc.mojodescriptor.optionalParameters" ), - optionalParams, w ); + optionalParams, w, goal ); } } @@ -698,7 +720,7 @@ private void writeParameterSummary( List parameterList, XMLWriter w ) * @param parameterList not null * @param w not null */ - private void writeParameterList( String title, List parameterList, XMLWriter w ) + private void writeParameterList( String title, List parameterList, XMLWriter w, String goal ) { w.startElement( "subsection" ); w.addAttribute( "name", title ); @@ -750,13 +772,15 @@ private void writeParameterList( String title, List parameterList, XM // 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 { @@ -883,4 +907,38 @@ private String escapeXml( String text ) 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 05903058b..2a961ad32 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 @@ 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 @@ void testGetShortType() assertEquals("Map", PluginXdocGenerator.getShortType( "java.util.Map" ) ); assertEquals("List<...>", PluginXdocGenerator.getShortType( "java.util.List>" ) ); } + + @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 = "testExternal Link..and a secondlinkend"; + assertEquals( externalLink, xdocGenerator.getXhtmlWithValidatedLinks( externalLink, "test" ) ); + assertEquals( externalLink, xdocGeneratorWithDisabledLinkValidator.getXhtmlWithValidatedLinks( externalLink, "test" ) ); + String validInternalLink = "testInternal Link..and a secondlinkend"; + assertEquals( validInternalLink, xdocGenerator.getXhtmlWithValidatedLinks( validInternalLink, "test" ) ); + assertEquals( validInternalLink, xdocGeneratorWithDisabledLinkValidator.getXhtmlWithValidatedLinks( validInternalLink, "test" ) ); + String invalidInternalLink = "testInternal Link..and a secondlinkend"; + String sanitizedInvalidInternalLink = "testInternal Link..and a secondlinkend"; + assertEquals( sanitizedInvalidInternalLink, xdocGenerator.getXhtmlWithValidatedLinks( invalidInternalLink, "test" ) ); + assertEquals( invalidInternalLink, xdocGeneratorWithDisabledLinkValidator.getXhtmlWithValidatedLinks( invalidInternalLink, "test" ) ); + } }