Skip to content

Commit

Permalink
[MPLUGIN-433] Allow to disable link validation
Browse files Browse the repository at this point in the history
Also validate internal links to javadocs in mojo/parameter description
and deprecated info
  • Loading branch information
kwin committed Oct 27, 2022
1 parent a191579 commit a0a8dad
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 20 deletions.
Expand Up @@ -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}
*/
Expand Down Expand Up @@ -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 );
}
Expand Down
Expand Up @@ -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}
*/
Expand Down Expand Up @@ -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 );
}
Expand Down
Expand Up @@ -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;
}
}
}
5 changes: 5 additions & 0 deletions maven-plugin-tools-generators/pom.xml
Expand Up @@ -113,6 +113,11 @@
<artifactId>maven-plugin-testing-harness</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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( "<a href=\\\"([^\\\"]*)\\\">(.*?)</a>" );

private static final Logger LOG = LoggerFactory.getLogger( PluginXdocGenerator.class );

/**
* locale
*/
Expand All @@ -68,6 +81,8 @@ public class PluginXdocGenerator
*/
private final File reportOutputDirectory;

private final boolean disableInternalJavadocLinkValidation;

/**
* Default constructor using <code>Locale.ENGLISH</code> as locale.
* Used only in test cases.
Expand All @@ -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 )
Expand All @@ -103,6 +119,7 @@ public PluginXdocGenerator( MavenProject project, Locale locale, File reportOutp
this.locale = locale;
}
this.reportOutputDirectory = reportOutputDirectory;
this.disableInternalJavadocLinkValidation = disableInternalJavadocLinkValidation;
}


Expand Down Expand Up @@ -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
}

Expand All @@ -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
{
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -457,7 +475,7 @@ private List<Parameter> filterParameters( List<Parameter> parameterList )
* @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" ) );
Expand All @@ -470,17 +488,20 @@ private void writeParameterDetails( List<Parameter> 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
{
Expand Down Expand Up @@ -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() } );
Expand Down Expand Up @@ -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<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 );
}
}

Expand All @@ -698,7 +720,7 @@ private void writeParameterSummary( List<Parameter> parameterList, XMLWriter w )
* @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 );
Expand Down Expand Up @@ -750,13 +772,15 @@ private void writeParameterList( String title, List<Parameter> 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
{
Expand Down Expand Up @@ -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();
}
}
Expand Up @@ -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;
Expand Down Expand Up @@ -63,4 +64,22 @@ void testGetShortType()
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" ) );
}
}

0 comments on commit a0a8dad

Please sign in to comment.