Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ dependencies {
}

// PMD dependencies
implementation('net.sourceforge.pmd:pmd-java:6.0.0') {
implementation('net.sourceforge.pmd:pmd-java:7.8.0') {
exclude group: 'jaxen'
exclude group: 'xerces'
exclude group: 'junit'
Expand All @@ -102,8 +102,9 @@ dependencies {
}

// SpotBugs dependencies
implementation('com.github.spotbugs:spotbugs:4.2.0') {
implementation('com.github.spotbugs:spotbugs:4.8.6') {
exclude group: 'org.slf4j'
exclude group: 'ch.qos.logback'
}

// Scalastyle http://www.scalastyle.org/
Expand Down
102 changes: 0 additions & 102 deletions src/main/java/pl/touk/sputnik/processor/pmd/CollectorRenderer.java

This file was deleted.

164 changes: 81 additions & 83 deletions src/main/java/pl/touk/sputnik/processor/pmd/PmdProcessor.java
Original file line number Diff line number Diff line change
@@ -1,45 +1,41 @@
package pl.touk.sputnik.processor.pmd;

import com.google.common.base.Joiner;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSetFactory;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RulesetsFactoryUtils;
import net.sourceforge.pmd.benchmark.Benchmark;
import net.sourceforge.pmd.benchmark.Benchmarker;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.util.ResourceLoader;
import net.sourceforge.pmd.util.datasource.DataSource;

import net.sourceforge.pmd.PmdAnalysis;
import net.sourceforge.pmd.lang.document.FileId;
import net.sourceforge.pmd.lang.rule.RulePriority;

import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import net.sourceforge.pmd.reporting.Report;
import net.sourceforge.pmd.reporting.RuleViolation;
import pl.touk.sputnik.configuration.Configuration;
import pl.touk.sputnik.configuration.GeneralOption;
import pl.touk.sputnik.review.Review;
import pl.touk.sputnik.review.ReviewException;
import pl.touk.sputnik.review.ReviewProcessor;
import pl.touk.sputnik.review.ReviewResult;
import pl.touk.sputnik.review.Severity;
import pl.touk.sputnik.review.Violation;
import pl.touk.sputnik.review.filter.PmdFilter;
import pl.touk.sputnik.review.transformer.FileNameTransformer;

import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class PmdProcessor implements ReviewProcessor {
private static final char LINE_SEPARATOR = '\n';
private static final String SOURCE_NAME = "PMD";
private static final char PMD_INPUT_PATH_SEPARATOR = ',';
private Renderer renderer;
private static final String PMD_INPUT_PATH_SEPARATOR = ",";

@NotNull
private final Configuration config;
Expand All @@ -51,22 +47,41 @@ public PmdProcessor(Configuration configuration) {
@Nullable
@Override
public ReviewResult process(@NotNull Review review) {
List<String> filesToReview = review.getFiles(new PmdFilter(), new FileNameTransformer());
List<Path> filesToReview = review.getFiles(new PmdFilter(), new FileNameTransformer()).stream()
.map(file -> {
Path path = FileSystems.getDefault().getPath(file);
if (!path.toFile().exists()) {
throw new ReviewException("File [" + file + "] does not exist");
}
return path;
})
.collect(Collectors.toList());
if (filesToReview.isEmpty()) {
return null;
}

try {
PMDConfiguration configuration = new PMDConfiguration();
configuration.setReportFormat(CollectorRenderer.class.getCanonicalName());
configuration.setRuleSets(getRulesets());
configuration.setInputPaths(Joiner.on(PMD_INPUT_PATH_SEPARATOR).join(filesToReview));
doPMD(configuration);
configuration.setInputPathList(filesToReview);
Report report = doPMD(configuration);
return convertReportToReview(report);
} catch (RuntimeException e) {
log.error("PMD processing error. Something wrong with configuration or analyzed files are not in workspace.", e);
throw new ReviewException("PMD processing error", e);
}
return renderer != null ? ((CollectorRenderer)renderer).getReviewResult() : null;
}

@NotNull
private ReviewResult convertReportToReview(Report report) {
ReviewResult reviewResult = new ReviewResult();
boolean showDetails = Boolean.parseBoolean(config.getProperty(GeneralOption.PMD_SHOW_VIOLATION_DETAILS));
for (RuleViolation ruleViolation : report.getViolations()) {
String violationDescription = showDetails ? renderViolationDetails(ruleViolation) :ruleViolation.getDescription();
FileId myFileId = ruleViolation.getFileId();
reviewResult.add(new Violation(myFileId.getOriginalPath(), ruleViolation.getBeginLine(), violationDescription, convert(ruleViolation.getRule().getPriority())));
}
return reviewResult;
}

@NotNull
Expand All @@ -75,74 +90,57 @@ public String getName() {
return SOURCE_NAME;
}

@Nullable
private String getRulesets() {
private List<String> getRulesets() {
String ruleSets = config.getProperty(GeneralOption.PMD_RULESETS);
log.info("Using PMD rulesets {}", ruleSets);
return ruleSets;
if (ruleSets == null) {
return new ArrayList<>();
}
return Arrays.asList(ruleSets.split(PMD_INPUT_PATH_SEPARATOR));
}

/**
* PMD has terrible design of process configuration. You must use report file with it. I paste this method here and
* improve it.
* Run PMD analysis
*
* @throws IllegalArgumentException
* if the configuration is not correct
* @return Report from PMD
* @throws IllegalArgumentException if the configuration is not correct
*/
private void doPMD(@NotNull PMDConfiguration configuration) throws IllegalArgumentException {
// Load the RuleSets
RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration, new ResourceLoader());

RuleSets ruleSets = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), ruleSetFactory);
// this is just double check - we don't get null here
// instead IllegalArgumentException/RuntimeException is thrown if configuration is wrong
if (ruleSets == null) {
return;
@NotNull
private Report doPMD(@NotNull PMDConfiguration configuration) throws IllegalArgumentException {
try (PmdAnalysis analysis = PmdAnalysis.create(configuration)) {
return analysis.performAnalysisAndCollectReport();
}
}

Set<Language> languages = getApplicableLanguages(configuration, ruleSets);
// this throws RuntimeException when modified file does not exist in workspace
List<DataSource> files = PMD.getApplicableFiles(configuration, languages);

long reportStart = System.nanoTime();
try {
renderer = configuration.createRenderer();
List<Renderer> renderers = new LinkedList<>();
renderers.add(renderer);
renderer.start();

Benchmarker.mark(Benchmark.Reporting, System.nanoTime() - reportStart, 0);

RuleContext ctx = new RuleContext();

PMD.processFiles(configuration, ruleSetFactory, files, ctx, renderers);

reportStart = System.nanoTime();
renderer.end();
} catch (IOException e) {
log.error("PMD analysis error", e);
} finally {
Benchmarker.mark(Benchmark.Reporting, System.nanoTime() - reportStart, 0);
@NotNull
private static Severity convert(@NotNull RulePriority rulePriority) {
switch (rulePriority) {
case HIGH:
return Severity.ERROR;
case MEDIUM_HIGH:
return Severity.WARNING;
case MEDIUM:
case MEDIUM_LOW:
return Severity.INFO;
case LOW:
return Severity.IGNORE;
default:
throw new IllegalArgumentException("RulePriority " + rulePriority + " is not supported");
}
}

/**
* Paste from PMD
*/
private static Set<Language> getApplicableLanguages(PMDConfiguration configuration, RuleSets ruleSets) {
Set<Language> languages = new HashSet<>();
LanguageVersionDiscoverer discoverer = configuration.getLanguageVersionDiscoverer();

for (Rule rule : ruleSets.getAllRules()) {
Language language = rule.getLanguage();
if (languages.contains(language))
continue;
LanguageVersion version = discoverer.getDefaultLanguageVersion(language);
if (RuleSet.applies(rule, version)) {
languages.add(language);
log.debug("Using {} version: {}", language.getShortName(), version.getShortName());
}
private static String renderViolationDetails(RuleViolation ruleViolation) {
StringBuilder fullDescription = new StringBuilder(ruleViolation.getDescription());

String reason = ruleViolation.getRule().getDescription();
if (StringUtils.isNotEmpty(reason)) {
fullDescription.append(LINE_SEPARATOR).append(reason);
}
String url = ruleViolation.getRule().getExternalInfoUrl();
if (StringUtils.isNotEmpty(url)) {
fullDescription.append(LINE_SEPARATOR).append(url);
}
return languages;

return fullDescription.toString();
}
}
Loading