Skip to content

Commit 4ba7ce4

Browse files
Merge pull request #84 from refactorfirst/upgrade-to-new-pmd
2 parents a6f0893 + 29aca7b commit 4ba7ce4

File tree

24 files changed

+3388
-838
lines changed

24 files changed

+3388
-838
lines changed

.github/workflows/maven.yml

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,29 @@ jobs:
1313

1414
steps:
1515
- name: Check out Git repository
16-
uses: actions/checkout@v2
16+
uses: actions/checkout@v3
1717

18-
- name: Set up JDK 1.8
19-
uses: actions/setup-java@v1
18+
- name: Set up JDK 11
19+
uses: actions/setup-java@v3
2020
with:
2121
java-version: 11
22+
distribution: 'zulu'
2223

2324
- name: Build With Maven
2425
env:
2526
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
2627
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
27-
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
28+
run: mvn -B verify #org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
2829

2930

3031
# Comment "Build With Maven" and uncomment the below when you want a snapshot build to be deployed
3132
# *********Don't forget to switch to Java 1.8 as well********
32-
# - name: Publish Maven snapshot
33-
# uses: samuelmeuli/action-maven-publish@v1
34-
# with:
35-
# gpg_private_key: ${{ secrets.gpg_private_key }}
36-
# gpg_passphrase: ${{ secrets.gpg_passphrase }}
37-
# nexus_username: ${{ secrets.nexus_username }}
38-
# nexus_password: ${{ secrets.nexus_password }}
39-
# maven_profiles: snapshot-release
33+
- name: Publish Maven snapshot
34+
uses: samuelmeuli/action-maven-publish@v1
35+
with:
36+
gpg_private_key: ${{ secrets.gpg_private_key }}
37+
gpg_passphrase: ${{ secrets.gpg_passphrase }}
38+
nexus_username: ${{ secrets.nexus_username }}
39+
nexus_password: ${{ secrets.nexus_password }}
40+
maven_profiles: snapshot-release
41+
maven_args: -B

cost-benefit-calculator/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
<artifactId>cost-benefit-calculator</artifactId>
1313

1414
<dependencies>
15+
<!-- Needed for PMD -->
16+
<dependency>
17+
<groupId>org.slf4j</groupId>
18+
<artifactId>slf4j-api</artifactId>
19+
<version>2.0.7</version>
20+
</dependency>
21+
1522
<dependency>
1623
<groupId>org.hjug.refactorfirst.changepronenessranker</groupId>
1724
<artifactId>change-proneness-ranker</artifactId>
Lines changed: 105 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,56 @@
11
package org.hjug.cbc;
22

3-
import java.io.ByteArrayInputStream;
4-
import java.io.ByteArrayOutputStream;
3+
import static net.sourceforge.pmd.RuleViolation.CLASS_NAME;
4+
import static net.sourceforge.pmd.RuleViolation.PACKAGE_NAME;
5+
56
import java.io.File;
67
import java.io.IOException;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.nio.file.Paths;
711
import java.util.*;
812
import java.util.stream.Collectors;
13+
import java.util.stream.Stream;
914
import lombok.extern.slf4j.Slf4j;
15+
import net.sourceforge.pmd.*;
16+
import net.sourceforge.pmd.lang.LanguageRegistry;
1017
import org.eclipse.jgit.api.errors.GitAPIException;
1118
import org.eclipse.jgit.lib.Repository;
1219
import org.hjug.git.ChangePronenessRanker;
1320
import org.hjug.git.GitLogReader;
1421
import org.hjug.git.RepositoryLogReader;
1522
import org.hjug.git.ScmLogInfo;
1623
import org.hjug.metrics.*;
24+
import org.hjug.metrics.rules.CBORule;
1725

1826
@Slf4j
1927
public class CostBenefitCalculator {
2028

21-
Map<String, ByteArrayOutputStream> filesToScan = new HashMap<>();
29+
private Report report;
30+
private String projBaseDir = null;
31+
32+
// copied from PMD's PmdTaskImpl.java and modified
33+
public void runPmdAnalysis(String projectBaseDir) throws IOException {
34+
projBaseDir = projectBaseDir;
35+
PMDConfiguration configuration = new PMDConfiguration();
36+
37+
try (PmdAnalysis pmd = PmdAnalysis.create(configuration)) {
38+
RuleSetLoader rulesetLoader = pmd.newRuleSetLoader();
39+
pmd.addRuleSets(rulesetLoader.loadRuleSetsWithoutException(List.of("category/java/design.xml")));
40+
41+
Rule cboClassRule = new CBORule();
42+
cboClassRule.setLanguage(LanguageRegistry.PMD.getLanguageByFullName("Java"));
43+
pmd.addRuleSet(RuleSet.forSingleRule(cboClassRule));
44+
45+
log.info("files to be scanned: " + Paths.get(projectBaseDir));
46+
47+
try (Stream<Path> files = Files.walk(Paths.get(projectBaseDir))) {
48+
files.forEach(file -> pmd.files().addFile(file));
49+
}
50+
51+
report = pmd.performAnalysisAndCollectReport();
52+
}
53+
}
2254

2355
public List<RankedDisharmony> calculateGodClassCostBenefitValues(String repositoryPath) {
2456

@@ -27,11 +59,16 @@ public List<RankedDisharmony> calculateGodClassCostBenefitValues(String reposito
2759
log.info("Initiating Cost Benefit calculation");
2860
try {
2961
repository = repositoryLogReader.gitRepository(new File(repositoryPath));
62+
for (String file :
63+
repositoryLogReader.listRepositoryContentsAtHEAD(repository).keySet()) {
64+
log.info("Files at HEAD: {}", file);
65+
}
3066
} catch (IOException e) {
3167
log.error("Failure to access Git repository", e);
3268
}
3369

34-
List<GodClass> godClasses = getGodClasses(getFilesToScan(repositoryLogReader, repository));
70+
// pass repo path here, not ByteArrayOutputStream
71+
List<GodClass> godClasses = getGodClasses();
3572

3673
List<ScmLogInfo> scmLogInfos = getRankedChangeProneness(repositoryLogReader, repository, godClasses);
3774

@@ -40,52 +77,70 @@ public List<RankedDisharmony> calculateGodClassCostBenefitValues(String reposito
4077

4178
List<RankedDisharmony> rankedDisharmonies = new ArrayList<>();
4279
for (GodClass godClass : godClasses) {
43-
rankedDisharmonies.add(new RankedDisharmony(godClass, rankedLogInfosByPath.get(godClass.getFileName())));
80+
if (rankedLogInfosByPath.containsKey(godClass.getFileName())) {
81+
rankedDisharmonies.add(
82+
new RankedDisharmony(godClass, rankedLogInfosByPath.get(godClass.getFileName())));
83+
}
84+
}
85+
86+
rankedDisharmonies.sort(
87+
Comparator.comparing(RankedDisharmony::getRawPriority).reversed());
88+
89+
int godClassPriority = 1;
90+
for (RankedDisharmony rankedGodClassDisharmony : rankedDisharmonies) {
91+
rankedGodClassDisharmony.setPriority(godClassPriority++);
4492
}
4593

4694
return rankedDisharmonies;
4795
}
4896

97+
private List<GodClass> getGodClasses() {
98+
List<GodClass> godClasses = new ArrayList<>();
99+
for (RuleViolation violation : report.getViolations()) {
100+
if (violation.getRule().getName().contains("GodClass")) {
101+
GodClass godClass = new GodClass(
102+
violation.getAdditionalInfo().get(CLASS_NAME),
103+
getFileName(violation),
104+
violation.getAdditionalInfo().get(PACKAGE_NAME),
105+
violation.getDescription());
106+
log.info("God Class identified: {}", godClass.getFileName());
107+
godClasses.add(godClass);
108+
}
109+
}
110+
111+
GodClassRanker godClassRanker = new GodClassRanker();
112+
godClassRanker.rankGodClasses(godClasses);
113+
114+
return godClasses;
115+
}
116+
49117
<T extends Disharmony> List<ScmLogInfo> getRankedChangeProneness(
50118
RepositoryLogReader repositoryLogReader, Repository repository, List<T> disharmonies) {
51119
List<ScmLogInfo> scmLogInfos = new ArrayList<>();
52-
log.info("Calculating Change Proneness for each God Class");
120+
log.info("Calculating Change Proneness");
53121
for (Disharmony disharmony : disharmonies) {
54122
String path = disharmony.getFileName();
55123
ScmLogInfo scmLogInfo = null;
56124
try {
57125
scmLogInfo = repositoryLogReader.fileLog(repository, path);
126+
log.info("Successfully fetched scmLogInfo for {}", scmLogInfo.getPath());
58127
} catch (GitAPIException | IOException e) {
59-
log.error("Error reading Git repository contents", e);
128+
log.error("Error reading Git repository contents.", e);
129+
} catch (NullPointerException e) {
130+
log.error("Encountered nested class in a class containing a violation. Class: {}", path);
60131
}
61132

62-
scmLogInfos.add(scmLogInfo);
133+
if (null != scmLogInfo) {
134+
log.info("adding {}", scmLogInfo.getPath());
135+
scmLogInfos.add(scmLogInfo);
136+
}
63137
}
64138

65139
ChangePronenessRanker changePronenessRanker = new ChangePronenessRanker(repository, repositoryLogReader);
66140
changePronenessRanker.rankChangeProneness(scmLogInfos);
67141
return scmLogInfos;
68142
}
69143

70-
private List<GodClass> getGodClasses(Map<String, ByteArrayOutputStream> filesToScan) {
71-
PMDGodClassRuleRunner ruleRunner = new PMDGodClassRuleRunner();
72-
73-
log.info("Identifying God Classes from files in repository");
74-
List<GodClass> godClasses = new ArrayList<>();
75-
for (Map.Entry<String, ByteArrayOutputStream> entry : filesToScan.entrySet()) {
76-
String filePath = entry.getKey();
77-
ByteArrayOutputStream value = entry.getValue();
78-
79-
ByteArrayInputStream inputStream = new ByteArrayInputStream(value.toByteArray());
80-
Optional<GodClass> godClassOptional = ruleRunner.runGodClassRule(filePath, inputStream);
81-
godClassOptional.ifPresent(godClasses::add);
82-
}
83-
84-
GodClassRanker godClassRanker = new GodClassRanker();
85-
godClassRanker.rankGodClasses(godClasses);
86-
return godClasses;
87-
}
88-
89144
public List<RankedDisharmony> calculateCBOCostBenefitValues(String repositoryPath) {
90145

91146
RepositoryLogReader repositoryLogReader = new GitLogReader();
@@ -97,7 +152,7 @@ public List<RankedDisharmony> calculateCBOCostBenefitValues(String repositoryPat
97152
log.error("Failure to access Git repository", e);
98153
}
99154

100-
List<CBOClass> cboClasses = getCBOClasses(getFilesToScan(repositoryLogReader, repository));
155+
List<CBOClass> cboClasses = getCBOClasses();
101156

102157
List<ScmLogInfo> scmLogInfos = getRankedChangeProneness(repositoryLogReader, repository, cboClasses);
103158

@@ -109,37 +164,35 @@ public List<RankedDisharmony> calculateCBOCostBenefitValues(String repositoryPat
109164
rankedDisharmonies.add(new RankedDisharmony(cboClass, rankedLogInfosByPath.get(cboClass.getFileName())));
110165
}
111166

112-
return rankedDisharmonies;
113-
}
167+
rankedDisharmonies.sort(
168+
Comparator.comparing(RankedDisharmony::getRawPriority).reversed());
114169

115-
private List<CBOClass> getCBOClasses(Map<String, ByteArrayOutputStream> filesToScan) {
170+
int cboPriority = 1;
171+
for (RankedDisharmony rankedCBODisharmony : rankedDisharmonies) {
172+
rankedCBODisharmony.setPriority(cboPriority++);
173+
}
116174

117-
CBORuleRunner ruleRunner = new CBORuleRunner();
175+
return rankedDisharmonies;
176+
}
118177

119-
log.info("Identifying highly coupled classes from files in repository");
178+
private List<CBOClass> getCBOClasses() {
120179
List<CBOClass> cboClasses = new ArrayList<>();
121-
for (Map.Entry<String, ByteArrayOutputStream> entry : filesToScan.entrySet()) {
122-
String filePath = entry.getKey();
123-
ByteArrayOutputStream value = entry.getValue();
124-
125-
ByteArrayInputStream inputStream = new ByteArrayInputStream(value.toByteArray());
126-
Optional<CBOClass> godClassOptional = ruleRunner.runCBOClassRule(filePath, inputStream);
127-
godClassOptional.ifPresent(cboClasses::add);
180+
for (RuleViolation violation : report.getViolations()) {
181+
if (violation.getRule().getName().contains("CBORule")) {
182+
log.info(violation.getDescription());
183+
CBOClass godClass = new CBOClass(
184+
violation.getAdditionalInfo().get(CLASS_NAME),
185+
getFileName(violation),
186+
violation.getAdditionalInfo().get(PACKAGE_NAME),
187+
violation.getDescription());
188+
log.info("Highly Coupled class identified: {}", godClass.getFileName());
189+
cboClasses.add(godClass);
190+
}
128191
}
129-
130192
return cboClasses;
131193
}
132194

133-
private Map<String, ByteArrayOutputStream> getFilesToScan(
134-
RepositoryLogReader repositoryLogReader, Repository repository) {
135-
136-
try {
137-
if (filesToScan.isEmpty()) {
138-
filesToScan = repositoryLogReader.listRepositoryContentsAtHEAD(repository);
139-
}
140-
} catch (IOException e) {
141-
log.error("Error reading Git repository contents", e);
142-
}
143-
return filesToScan;
195+
private String getFileName(RuleViolation violation) {
196+
return violation.getFileId().getUriString().replace("file:///" + projBaseDir.replace("\\", "/") + "/", "");
144197
}
145198
}

cost-benefit-calculator/src/main/java/org/hjug/cbc/RankedDisharmony.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class RankedDisharmony {
1616
private final Integer effortRank;
1717
private final Integer changePronenessRank;
1818
private final Integer rawPriority;
19-
private Integer priority;
19+
private Integer priority = 0;
2020

2121
private Integer wmc;
2222
private Integer wmcRank;

0 commit comments

Comments
 (0)