Skip to content

Commit

Permalink
Merge pull request #59 from adangel:MPMD-309
Browse files Browse the repository at this point in the history
[MPMD-309] Add configuration option to show suppressed violations
  • Loading branch information
adangel committed Mar 24, 2022
2 parents 5710688 + 3816c66 commit 38d5c47
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 2 deletions.
Expand Up @@ -22,12 +22,15 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.codehaus.plexus.util.StringUtils;

import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.Report.SuppressedViolation;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.util.datasource.DataSource;
Expand All @@ -41,8 +44,9 @@
*/
public class PmdCollectingRenderer extends AbstractRenderer
{
private List<ProcessingError> errors = Collections.synchronizedList( new ArrayList<ProcessingError>() );
private List<RuleViolation> violations = Collections.synchronizedList( new ArrayList<RuleViolation>() );
private List<ProcessingError> errors = Collections.synchronizedList( new ArrayList<>() );
private List<RuleViolation> violations = Collections.synchronizedList( new ArrayList<>() );
private List<SuppressedViolation> suppressed = Collections.synchronizedList( new ArrayList<> () );

/**
* Collects all reports from all threads.
Expand All @@ -57,6 +61,7 @@ public void renderFileReport( Report report ) throws IOException
{
violations.addAll( report.getViolations() );
errors.addAll( report.getProcessingErrors() );
suppressed.addAll( report.getSuppressedViolations() );
}

/**
Expand Down Expand Up @@ -129,6 +134,19 @@ public Report asReport()
{
report.addError( e );
}
Map<Integer, String> suppressedLines = new HashMap<Integer, String>();
for ( SuppressedViolation s : suppressed )
{
if ( s.suppressedByNOPMD() )
{
suppressedLines.put( s.getRuleViolation().getBeginLine(), s.getUserMessage() );
}
}
report.suppress( suppressedLines );
for ( SuppressedViolation s : suppressed )
{
report.addRuleViolation( s.getRuleViolation() );
}
return report;
}

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/apache/maven/plugins/pmd/PmdReport.java
Expand Up @@ -216,6 +216,14 @@ public class PmdReport
@Parameter( property = "pmd.renderViolationsByPriority", defaultValue = "true" )
private boolean renderViolationsByPriority = true;

/**
* Add a section in the HTML report that lists the suppressed violations.
*
* @since 3.17.0
*/
@Parameter( property = "pmd.renderSuppressedViolations", defaultValue = "true" )
private boolean renderSuppressedViolations = true;

/**
* Before PMD is executed, the configured rulesets are resolved and copied into this directory.
* <p>Note: Before 3.13.0, this was by default ${project.build.directory}.
Expand Down Expand Up @@ -501,6 +509,10 @@ private void generateMavenSiteReport( Locale locale )
doxiaRenderer.setRenderViolationsByPriority( renderViolationsByPriority );
doxiaRenderer.setFiles( filesToProcess );
doxiaRenderer.setViolations( pmdResult.getViolations() );
if ( renderSuppressedViolations )
{
doxiaRenderer.setSuppressedViolations( pmdResult.getSuppressedViolations() );
}
if ( renderProcessingErrors )
{
doxiaRenderer.setProcessingErrors( pmdResult.getErrors() );
Expand Down
80 changes: 80 additions & 0 deletions src/main/java/org/apache/maven/plugins/pmd/PmdReportGenerator.java
Expand Up @@ -35,6 +35,7 @@
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.pmd.model.ProcessingError;
import org.apache.maven.plugins.pmd.model.SuppressedViolation;
import org.apache.maven.plugins.pmd.model.Violation;
import org.codehaus.plexus.util.StringUtils;

Expand All @@ -58,6 +59,8 @@ public class PmdReportGenerator

private Set<Violation> violations = new HashSet<>();

private List<SuppressedViolation> suppressedViolations = new ArrayList<>();

private List<ProcessingError> processingErrors = new ArrayList<>();

private boolean aggregate;
Expand Down Expand Up @@ -93,6 +96,11 @@ public List<Violation> getViolations()
return new ArrayList<>( violations );
}

public void setSuppressedViolations( Collection<SuppressedViolation> suppressedViolations )
{
this.suppressedViolations = new ArrayList<>( suppressedViolations );
}

public void setProcessingErrors( Collection<ProcessingError> errors )
{
this.processingErrors = new ArrayList<>( errors );
Expand Down Expand Up @@ -377,6 +385,73 @@ private void outputLineLink( int line, PmdFileInfo fileInfo )
}
}

// PMD might run the analysis multi-threaded, so the suppressed violations might be reported
// out of order. We sort them here by filename before writing them to
// the report.
private void renderSuppressedViolations()
throws IOException
{
sink.section1();
sink.sectionTitle1();
sink.text( bundle.getString( "report.pmd.suppressedViolations.title" ) );
sink.sectionTitle1_();

Collections.sort( suppressedViolations, new Comparator<SuppressedViolation>()
{
@Override
public int compare( SuppressedViolation o1, SuppressedViolation o2 )
{
return o1.getFilename().compareTo( o2.getFilename() );
}
} );

sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.pmd.suppressedViolations.column.filename" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.pmd.suppressedViolations.column.ruleMessage" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.pmd.suppressedViolations.column.suppressionType" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.pmd.suppressedViolations.column.userMessage" ) );
sink.tableHeaderCell_();
sink.tableRow_();

for ( SuppressedViolation suppressedViolation : suppressedViolations )
{
String filename = suppressedViolation.getFilename();
PmdFileInfo fileInfo = determineFileInfo( filename );
filename = shortenFilename( filename, fileInfo );

sink.tableRow();

sink.tableCell();
sink.text( filename );
sink.tableCell_();

sink.tableCell();
sink.text( suppressedViolation.getRuleMessage() );
sink.tableCell_();

sink.tableCell();
sink.text( suppressedViolation.getSuppressionType() );
sink.tableCell_();

sink.tableCell();
sink.text( suppressedViolation.getUserMessage() );
sink.tableCell_();

sink.tableRow_();
}

sink.table_();
sink.section1_();
}

private void processProcessingErrors() throws IOException
{
// sort the problem by filename first, since PMD is executed multi-threaded
Expand Down Expand Up @@ -492,6 +567,11 @@ public void render()
sink.paragraph_();
}

if ( !suppressedViolations.isEmpty() )
{
renderSuppressedViolations();
}

if ( !processingErrors.isEmpty() )
{
processProcessingErrors();
Expand Down
Expand Up @@ -32,6 +32,7 @@
import org.apache.maven.plugins.pmd.model.PmdErrorDetail;
import org.apache.maven.plugins.pmd.model.PmdFile;
import org.apache.maven.plugins.pmd.model.ProcessingError;
import org.apache.maven.plugins.pmd.model.SuppressedViolation;
import org.apache.maven.plugins.pmd.model.Violation;
import org.apache.maven.plugins.pmd.model.io.xpp3.PmdXpp3Reader;
import org.apache.maven.reporting.MavenReportException;
Expand All @@ -43,6 +44,7 @@ public class PmdResult
{
private final List<ProcessingError> processingErrors = new ArrayList<>();
private final List<Violation> violations = new ArrayList<>();
private final List<SuppressedViolation> suppressedViolations = new ArrayList<>();

public static final PmdResult EMPTY = new PmdResult();

Expand All @@ -68,6 +70,7 @@ private void loadResult( File pmdFile, String encoding ) throws MavenReportExcep
PmdXpp3Reader reader = new PmdXpp3Reader();
PmdErrorDetail details = reader.read( reader1, false );
processingErrors.addAll( details.getErrors() );
suppressedViolations.addAll( details.getSuppressedViolations() );

for ( PmdFile file : details.getFiles() )
{
Expand Down Expand Up @@ -147,6 +150,11 @@ public Collection<Violation> getViolations()
return violations;
}

public Collection<SuppressedViolation> getSuppressedViolations()
{
return suppressedViolations;
}

public Collection<ProcessingError> getErrors()
{
return processingErrors;
Expand Down
28 changes: 28 additions & 0 deletions src/main/mdo/pmd.mdo
Expand Up @@ -45,6 +45,13 @@ under the License.
<multiplicity>*</multiplicity>
</association>
</field>
<field>
<name>suppressedViolations</name>
<association xml.tagName="suppressedviolation" xml.itemsStyle="flat">
<type>SuppressedViolation</type>
<multiplicity>*</multiplicity>
</association>
</field>
<field>
<name>errors</name>
<association xml.tagName="error" xml.itemsStyle="flat">
Expand Down Expand Up @@ -126,6 +133,27 @@ under the License.
</codeSegment>
</codeSegments>
</class>
<class>
<name>SuppressedViolation</name>
<fields>
<field xml.attribute="true">
<name>filename</name>
<type>String</type>
</field>
<field xml.tagName="suppressiontype" xml.attribute="true">
<name>suppressionType</name>
<type>String</type>
</field>
<field xml.tagName="msg" xml.attribute="true">
<name>ruleMessage</name>
<type>String</type>
</field>
<field xml.tagName="usermsg" xml.attribute="true">
<name>userMessage</name>
<type>String</type>
</field>
</fields>
</class>
<class>
<name>ProcessingError</name>
<fields>
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/pmd-report.properties
Expand Up @@ -27,6 +27,11 @@ report.pmd.files=Files
report.pmd.violationsByPriority=Violations By Priority
report.pmd.priority=Priority
report.pmd.noProblems=PMD found no problems in your source code.
report.pmd.suppressedViolations.title=Suppressed Violations
report.pmd.suppressedViolations.column.filename=Filename
report.pmd.suppressedViolations.column.ruleMessage=Rule message
report.pmd.suppressedViolations.column.suppressionType=Suppression type
report.pmd.suppressedViolations.column.userMessage=Reason
report.pmd.processingErrors.title=Processing Errors
report.pmd.processingErrors.column.filename=Filename
report.pmd.processingErrors.column.problem=Problem
5 changes: 5 additions & 0 deletions src/main/resources/pmd-report_de.properties
Expand Up @@ -27,6 +27,11 @@ report.pmd.files=Dateien
report.pmd.violationsByPriority=Verst\u00f6\u00dfe nach Priorit\u00e4t
report.pmd.priority=Priorit\u00e4t
report.pmd.noProblems=PMD hat keine Probleme in dem Quellcode gefunden.
report.pmd.suppressedViolations.title=Unterdr\u00fcckte Verst\u00f6\u00dfe
report.pmd.suppressedViolations.column.filename=Datei
report.pmd.suppressedViolations.column.ruleMessage=Regel
report.pmd.suppressedViolations.column.suppressionType=Art der Unterdr\u00fcckung
report.pmd.suppressedViolations.column.userMessage=Grund
report.pmd.processingErrors.title=Verarbeitungsprobleme
report.pmd.processingErrors.column.filename=Datei
report.pmd.processingErrors.column.problem=Problem
45 changes: 45 additions & 0 deletions src/test/java/org/apache/maven/plugins/pmd/PmdReportTest.java
Expand Up @@ -530,6 +530,51 @@ public void testSuppressMarkerConfiguration()
String str = readFile( generatedFile );

// check that there is no violation reported for "unusedVar2" - as it is suppressed
assertFalse( str.contains( "Avoid unused private fields such as 'unusedVar2'.\n </violation>" ) );
// but it appears as suppressed
assertTrue( str.contains( "suppressiontype=\"nopmd\" msg=\"Avoid unused private fields such as 'unusedVar2'.\"" ));

generatedFile = new File( getBasedir(), "target/test/unit/default-configuration/target/site/pmd.html" );
renderer( mojo, generatedFile );
assertTrue( FileUtils.fileExists( generatedFile.getAbsolutePath() ) );

// check if there's a link to the JXR files
str = readFile( generatedFile );

assertTrue( str.contains( "/xref/def/configuration/AppSample.html#L27" ) );
// suppressed violation
assertTrue( str.contains( "Avoid unused private fields such as 'unusedVar2'." ) );
}

public void testSuppressMarkerConfigurationWithoutRendering()
throws Exception
{
File testPom =
new File( getBasedir(),
"src/test/resources/unit/default-configuration/pmd-with-suppressMarker-no-render-plugin-config.xml" );
PmdReport mojo = (PmdReport) lookupMojo( "pmd", testPom );
mojo.execute();

// check if the PMD files were generated
File generatedFile = new File( getBasedir(), "target/test/unit/default-configuration/target/pmd.xml" );
assertTrue( FileUtils.fileExists( generatedFile.getAbsolutePath() ) );

String str = readFile( generatedFile );

// check that there is no violation reported for "unusedVar2" - as it is suppressed
assertFalse( str.contains( "Avoid unused private fields such as 'unusedVar2'.\n </violation>" ) );
// but it appears as suppressed
assertTrue( str.contains( "suppressiontype=\"nopmd\" msg=\"Avoid unused private fields such as 'unusedVar2'.\"" ));

generatedFile = new File( getBasedir(), "target/test/unit/default-configuration/target/site/pmd.html" );
renderer( mojo, generatedFile );
assertTrue( FileUtils.fileExists( generatedFile.getAbsolutePath() ) );

// check if there's a link to the JXR files
str = readFile( generatedFile );

assertTrue( str.contains( "/xref/def/configuration/AppSample.html#L27" ) );
// suppressed violations are not rendered
assertFalse( str.contains( "Avoid unused private fields such as 'unusedVar2'." ) );
}

Expand Down

0 comments on commit 38d5c47

Please sign in to comment.