Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ application {


// Directories of interest
val pmdWrapperDistDir: String = layout.projectDirectory.dir("../dist/pmd-wrapper").asFile.path;
val pmdCpdWrappersDistDir: String = layout.projectDirectory.dir("../dist/pmd-cpd-wrappers").asFile.path;
val reportsDir: String = layout.buildDirectory.dir("reports").get().asFile.path


Expand All @@ -59,7 +59,7 @@ tasks.distTar {
// During assemble, we want to run the installDist task (which comes from the distribution plugin which comes from the application plugin)
// instead ... but first we need to modify the location where the jar files should be placed.
tasks.installDist {
into(pmdWrapperDistDir)
into(pmdCpdWrappersDistDir)
includeEmptyDirs = false
}
tasks.assemble {
Expand Down Expand Up @@ -102,9 +102,9 @@ tasks.register("showCoverageReport") {


// ======== CLEAN RELATED TASKS ========================================================================================
tasks.register<Delete>("deletePmdWrapperFromDist") {
delete(pmdWrapperDistDir)
tasks.register<Delete>("deletePmdCpdWrappersFromDist") {
delete(pmdCpdWrappersDistDir)
}
tasks.named("clean") {
dependsOn("deletePmdWrapperFromDist")
dependsOn("deletePmdCpdWrappersFromDist")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.salesforce.sfca.cpdwrapper;

import java.util.ArrayList;
import java.util.List;

/**
* Java object to help build cpd results for a specific language.
* We will serialize Map<String, CpdLanguageRunResults> to json to create the overall results for all languages.
*/
public class CpdLanguageRunResults {
public List<Match> matches = new ArrayList<>();
public List<ProcessingError> processingErrors = new ArrayList<>();

public static class Match {
public int numTokensInBlock;
public int numNonemptyLinesInBlock;
public int numBlocks;
public List<BlockLocation> blockLocations = new ArrayList<>();

public static class BlockLocation {
public String file;
public int startLine;
public int startCol;
public int endLine;
public int endCol;
}
}

public static class ProcessingError {
public String file;
public String message;
public String detail;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -25,10 +24,10 @@
* Class to help us invoke CPD - once for each language that should be processed
*/
class CpdRunner {
public Map<String, List<CpdMatch>> run(CpdRunInputData runInputData) throws IOException {
public Map<String, CpdLanguageRunResults> run(CpdRunInputData runInputData) throws IOException {
validateRunInputData(runInputData);

Map<String, List<CpdMatch>> results = new HashMap<>();
Map<String, CpdLanguageRunResults> results = new HashMap<>();

for (Map.Entry<String, List<String>> entry : runInputData.filesToScanPerLanguage.entrySet()) {
String language = entry.getKey();
Expand All @@ -37,17 +36,17 @@ public Map<String, List<CpdMatch>> run(CpdRunInputData runInputData) throws IOEx
continue;
}
List<Path> pathsToScan = filesToScan.stream().map(Paths::get).collect(Collectors.toList());
List<CpdMatch> languageMatches = runLanguage(language, pathsToScan, runInputData.minimumTokens, runInputData.skipDuplicateFiles);
CpdLanguageRunResults languageRunResults = runLanguage(language, pathsToScan, runInputData.minimumTokens, runInputData.skipDuplicateFiles);

if (!languageMatches.isEmpty()) {
results.put(language, languageMatches);
if (!languageRunResults.matches.isEmpty() || !languageRunResults.processingErrors.isEmpty()) {
results.put(language, languageRunResults);
}
}

return results;
}

private List<CpdMatch> runLanguage(String language, List<Path> pathsToScan, int minimumTokens, boolean skipDuplicateFiles) throws IOException {
private CpdLanguageRunResults runLanguage(String language, List<Path> pathsToScan, int minimumTokens, boolean skipDuplicateFiles) throws IOException {
// Note that the name "minimumTokens" comes from the public facing documentation and the cli but
// behind the scenes, it maps to MinimumTileSize. To learn more about the mappings to the config, see:
// https://github.com/pmd/pmd/blob/main/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java
Expand All @@ -62,23 +61,27 @@ private List<CpdMatch> runLanguage(String language, List<Path> pathsToScan, int
config.setSkipDuplicates(skipDuplicateFiles);
config.setReporter(new CpdErrorListener());

List<CpdMatch> cpdMatches = new ArrayList<>();
CpdLanguageRunResults languageRunResults = new CpdLanguageRunResults();

try (CpdAnalysis cpd = CpdAnalysis.create(config)) {
cpd.performAnalysis(report -> {
for (Report.ProcessingError processingError : report.getProcessingErrors()) {
// We don't expect any processing errors, but if there are any, then we can push them
// to stdOut so that they ultimately get logged. But we should continue as normal here.
System.out.println("Unexpected CPD processing error: " + processingError.getError().getMessage());

for (Report.ProcessingError reportProcessingError : report.getProcessingErrors()) {
CpdLanguageRunResults.ProcessingError processingErr = new CpdLanguageRunResults.ProcessingError();
processingErr.file = reportProcessingError.getFileId().getAbsolutePath();
processingErr.message = reportProcessingError.getMsg();
processingErr.detail = reportProcessingError.getDetail();
languageRunResults.processingErrors.add(processingErr);
}

for (Match match : report.getMatches()) {
CpdMatch cpdMatch = new CpdMatch();
CpdLanguageRunResults.Match cpdMatch = new CpdLanguageRunResults.Match();
cpdMatch.numBlocks = match.getMarkCount();
cpdMatch.numTokensInBlock = match.getTokenCount();
cpdMatch.numNonemptyLinesInBlock = match.getLineCount();

for (Mark mark : match.getMarkSet()) {
CpdMatch.BlockLocation blockLocation = new CpdMatch.BlockLocation();
CpdLanguageRunResults.Match.BlockLocation blockLocation = new CpdLanguageRunResults.Match.BlockLocation();
FileLocation location = mark.getLocation();
blockLocation.file = location.getFileId().getAbsolutePath();
blockLocation.startLine = location.getStartLine();
Expand All @@ -89,12 +92,12 @@ private List<CpdMatch> runLanguage(String language, List<Path> pathsToScan, int
cpdMatch.blockLocations.add(blockLocation);
}

cpdMatches.add(cpdMatch);
languageRunResults.matches.add(cpdMatch);
}
});
}

return cpdMatches;
return languageRunResults;
}

private void validateRunInputData(CpdRunInputData runInputData) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import com.google.gson.Gson;
Expand All @@ -29,24 +28,27 @@
* - {resultsOutputFile} is a JSON file to write CPD results to.
* Example:
* {
* "apex": [
* {
* "numTokensInBlock": 18,
* "numNonemptyLinesInBlock": 5,
* "numBlocks": 2,
* "blockLocations": [
* {
* "file": "/full/path/to/file1.cls",
* "startLine": 1, "startCol": 1, "endLine": 5, "endCol": 2
* },
* {
* "file": "/full/path/to/file2.cls",
* "startLine": 18, "startCol": 6, "endLine": 22, "endCol": 8
* }
* ]
* },
* ...
* ],
* "apex": {
* matches: [
* {
* "numTokensInBlock": 18,
* "numNonemptyLinesInBlock": 5,
* "numBlocks": 2,
* "blockLocations": [
* {
* "file": "/full/path/to/file1.cls",
* "startLine": 1, "startCol": 1, "endLine": 5, "endCol": 2
* },
* {
* "file": "/full/path/to/file2.cls",
* "startLine": 18, "startCol": 6, "endLine": 22, "endCol": 8
* }
* ]
* },
* ...
* ],
* processingErrors: []
* },
* "xml": ...
* }
*/
Expand Down Expand Up @@ -84,7 +86,7 @@ private static void invokeRunCommand(String[] args) {
}

CpdRunner cpdRunner = new CpdRunner();
Map<String, List<CpdMatch>> results;
Map<String, CpdLanguageRunResults> results;
try {
results = cpdRunner.run(inputData);
} catch (Exception e) {
Expand Down
Loading