Skip to content

Commit cb74556

Browse files
committed
Added Checkstyle support
1 parent 2ae8e5e commit cb74556

File tree

9 files changed

+332
-41
lines changed

9 files changed

+332
-41
lines changed

pom.xml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<developerConnection>scm:git:[email protected]:chkal/gitlab-code-quality-plugin.git</developerConnection>
2323
<url>https://github.com/chkal/gitlab-code-quality-plugin</url>
2424
</scm>
25-
25+
2626
<distributionManagement>
2727
<snapshotRepository>
2828
<id>ossrh</id>
@@ -70,6 +70,19 @@
7070
<packageName>de.chkal.maven.gitlab.codequality.spotbugs</packageName>
7171
</configuration>
7272
</execution>
73+
<execution>
74+
<id>schema-checkstyle</id>
75+
<goals>
76+
<goal>xjc</goal>
77+
</goals>
78+
<configuration>
79+
<sources>
80+
<source>src/main/xsd/checkstyle.xsd</source>
81+
</sources>
82+
<packageName>de.chkal.maven.gitlab.codequality.checkstyle</packageName>
83+
<clearOutputDir>false</clearOutputDir>
84+
</configuration>
85+
</execution>
7386
</executions>
7487
</plugin>
7588

@@ -150,6 +163,20 @@
150163
<version>2.10.1</version>
151164
</dependency>
152165

166+
<!-- Testing -->
167+
<dependency>
168+
<groupId>org.junit.jupiter</groupId>
169+
<artifactId>junit-jupiter</artifactId>
170+
<version>5.9.2</version>
171+
<scope>test</scope>
172+
</dependency>
173+
<dependency>
174+
<groupId>org.assertj</groupId>
175+
<artifactId>assertj-core</artifactId>
176+
<version>3.24.2</version>
177+
<scope>test</scope>
178+
</dependency>
179+
153180
</dependencies>
154181

155182
<profiles>
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package de.chkal.maven.gitlab.codequality;
22

3-
import java.io.File;
3+
import java.io.InputStream;
44
import java.util.List;
55

66
public interface FindingProvider {
77

8-
List<Finding> getFindings(File inputFile);
8+
String getName();
9+
10+
List<Finding> getFindings(InputStream stream);
911

1012
}

src/main/java/de/chkal/maven/gitlab/codequality/GenerateMojo.java

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package de.chkal.maven.gitlab.codequality;
22

3+
import de.chkal.maven.gitlab.codequality.checkstyle.CheckstyleFindingProvider;
34
import de.chkal.maven.gitlab.codequality.spotbugs.SpotbugsFindingProvider;
45
import java.io.File;
6+
import java.io.FileInputStream;
57
import java.io.FileOutputStream;
68
import java.io.IOException;
9+
import java.io.InputStream;
710
import java.util.ArrayList;
11+
import java.util.Collections;
812
import java.util.List;
913
import org.apache.maven.plugin.AbstractMojo;
10-
import org.apache.maven.plugin.MojoExecutionException;
1114
import org.apache.maven.plugin.MojoFailureException;
1215
import org.apache.maven.plugins.annotations.LifecyclePhase;
1316
import org.apache.maven.plugins.annotations.Mojo;
@@ -23,13 +26,19 @@ public class GenerateMojo extends AbstractMojo {
2326
@Parameter(defaultValue = "${project.build.directory}/spotbugsXml.xml")
2427
public File spotbugsInputFile;
2528

29+
@Parameter(defaultValue = "true")
30+
public boolean checkstyleEnabled;
31+
32+
@Parameter(defaultValue = "${project.build.directory}/checkstyle-result.xml")
33+
public File checkstyleInputFile;
34+
2635
@Parameter(defaultValue = "${project.build.directory}/gl-code-quality-report.json")
2736
public File outputFile;
2837

2938
@Parameter(defaultValue = "${project}", readonly = true, required = true)
3039
private MavenProject project;
3140

32-
public void execute() throws MojoExecutionException, MojoFailureException {
41+
public void execute() throws MojoFailureException {
3342

3443
Logger log = new Logger(getLog());
3544

@@ -38,41 +47,70 @@ public void execute() throws MojoExecutionException, MojoFailureException {
3847

3948
List<Finding> findings = new ArrayList<>();
4049

41-
// SpotBugs
42-
if (spotbugsEnabled) {
50+
// Run SpotBugs provider
51+
findings.addAll(executeProvider(
52+
new SpotbugsFindingProvider(project, repositoryRoot, log),
53+
spotbugsEnabled,
54+
spotbugsInputFile,
55+
log
56+
));
57+
58+
// Run Checkstyle provider
59+
findings.addAll(executeProvider(
60+
new CheckstyleFindingProvider(repositoryRoot),
61+
checkstyleEnabled,
62+
checkstyleInputFile,
63+
log
64+
));
65+
66+
// Create GitLab report
67+
if (findings.size() > 0) {
68+
try (FileOutputStream stream = new FileOutputStream(outputFile)) {
69+
new ReportSerializer().write(findings, stream);
70+
log.info("GitLab code quality report for {} issue created: {}",
71+
findings.size(), outputFile);
72+
} catch (IOException e) {
73+
throw new IllegalStateException(e);
74+
}
75+
}
76+
77+
}
4378

44-
if (spotbugsInputFile.canRead()) {
79+
private static List<Finding> executeProvider(FindingProvider provider,
80+
boolean active, File file, Logger log) throws MojoFailureException {
4581

46-
List<Finding> spotbugsFindings = new SpotbugsFindingProvider(project, repositoryRoot, log)
47-
.getFindings(spotbugsInputFile);
82+
// Checkstyle
83+
if (active) {
4884

49-
log.info("SpotBugs XML report with {} issues found: {}",
50-
spotbugsFindings.size(), spotbugsInputFile);
85+
if (file.canRead()) {
5186

52-
findings.addAll(spotbugsFindings);
87+
try (InputStream stream = new FileInputStream(file)) {
88+
89+
List<Finding> findings = provider.getFindings(stream);
90+
91+
log.info("{} report with {} issues found: {}", provider.getName(),
92+
findings.size(), file);
93+
94+
return findings;
95+
96+
} catch (IOException e) {
97+
throw new MojoFailureException("IO error", e);
98+
}
5399

54100
} else {
55-
log.info(String.format("SpotBugs XML report not found: %s", spotbugsInputFile));
101+
log.info("{} report not found: {}", provider.getName(), file);
56102
}
57103

58104
} else {
59-
log.info("SpotBugs support disabled.");
105+
log.info("{} support disabled.", provider.getName());
60106
}
61107

62-
// Create GitLab report
63-
if (findings.size() > 0) {
64-
try (FileOutputStream stream = new FileOutputStream(outputFile)) {
65-
new ReportSerializer().write(findings, stream);
66-
log.info("GitLab code quality report containing {} issue created: {}",
67-
findings.size(), outputFile);
68-
} catch (IOException e) {
69-
throw new IllegalStateException(e);
70-
}
71-
}
108+
return Collections.emptyList();
109+
72110

73111
}
74112

75-
private File getRepositoryRootDir(File initial, Logger log) {
113+
private static File getRepositoryRootDir(File initial, Logger log) {
76114

77115
File current = initial;
78116

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package de.chkal.maven.gitlab.codequality.checkstyle;
2+
3+
import de.chkal.maven.gitlab.codequality.Finding;
4+
import de.chkal.maven.gitlab.codequality.Finding.Severity;
5+
import de.chkal.maven.gitlab.codequality.FindingProvider;
6+
import jakarta.xml.bind.DatatypeConverter;
7+
import jakarta.xml.bind.JAXBContext;
8+
import jakarta.xml.bind.JAXBElement;
9+
import jakarta.xml.bind.JAXBException;
10+
import jakarta.xml.bind.Unmarshaller;
11+
import java.io.File;
12+
import java.io.InputStream;
13+
import java.nio.charset.StandardCharsets;
14+
import java.nio.file.Path;
15+
import java.security.MessageDigest;
16+
import java.security.NoSuchAlgorithmException;
17+
import java.util.List;
18+
import java.util.Locale;
19+
import java.util.stream.Collectors;
20+
import java.util.stream.Stream;
21+
import javax.xml.stream.XMLInputFactory;
22+
import javax.xml.stream.XMLStreamException;
23+
import javax.xml.stream.XMLStreamReader;
24+
25+
public class CheckstyleFindingProvider implements FindingProvider {
26+
27+
private final File repositoryRoot;
28+
29+
public CheckstyleFindingProvider(File repositoryRoot) {
30+
this.repositoryRoot = repositoryRoot;
31+
}
32+
33+
@Override
34+
public String getName() {
35+
return "Checkstyle";
36+
}
37+
38+
@Override
39+
public List<Finding> getFindings(InputStream stream) {
40+
41+
try {
42+
43+
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
44+
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(stream);
45+
46+
JAXBContext jaxbContext = JAXBContext.newInstance(CheckstyleType.class);
47+
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
48+
JAXBElement<CheckstyleType> checkstyleType =
49+
unmarshaller.unmarshal(xmlStreamReader, CheckstyleType.class);
50+
51+
return checkstyleType.getValue().getFile().stream()
52+
.flatMap(this::transformFileType)
53+
.collect(Collectors.toList());
54+
55+
} catch (JAXBException | XMLStreamException e) {
56+
throw new IllegalStateException(e);
57+
}
58+
}
59+
60+
private Stream<Finding> transformFileType(FileType fileType) {
61+
return fileType.getError().stream().map(errorType -> transformErrorType(fileType, errorType));
62+
}
63+
64+
private Finding transformErrorType(FileType fileType, ErrorType errorType) {
65+
66+
Finding finding = new Finding();
67+
finding.setDescription(String.format("%s: %s", getName(), errorType.getMessage()));
68+
finding.setSeverity(getSeverity(errorType.getSeverity()));
69+
finding.setPath(getRepositoryRelativePath(fileType));
70+
finding.setLine(getLineNumber(errorType));
71+
72+
// Checkstyle doesn't provide any fingerprints, so we create our own one
73+
autoGenerateFingerprint(finding);
74+
75+
return finding;
76+
77+
}
78+
79+
private String getRepositoryRelativePath(FileType fileType) {
80+
Path absolutePath = Path.of(fileType.getName());
81+
return repositoryRoot.toPath().relativize(absolutePath).toString();
82+
}
83+
84+
private Severity getSeverity(String severity) {
85+
switch (severity) {
86+
case "error":
87+
return Severity.MAJOR;
88+
case "warning":
89+
return Severity.MINOR;
90+
case "info":
91+
case "ignore":
92+
default:
93+
return Severity.INFO;
94+
}
95+
}
96+
97+
private void autoGenerateFingerprint(Finding finding) {
98+
99+
try {
100+
101+
String key = String.format("%s:%s:%d",
102+
finding.getDescription(), finding.getPath(), finding.getLine());
103+
104+
MessageDigest sha1Digest = MessageDigest.getInstance("SHA256");
105+
sha1Digest.update(key.getBytes(StandardCharsets.UTF_8));
106+
byte[] digest = sha1Digest.digest();
107+
108+
finding.setFingerprint(DatatypeConverter.printHexBinary(digest).toLowerCase(Locale.ROOT));
109+
110+
} catch (NoSuchAlgorithmException e) {
111+
throw new RuntimeException(e);
112+
}
113+
114+
}
115+
116+
private static Integer getLineNumber(ErrorType errorType) {
117+
return errorType.getLine() != null && errorType.getLine().matches("\\d+")
118+
? Integer.parseInt(errorType.getLine())
119+
: 1;
120+
}
121+
122+
}

src/main/java/de/chkal/maven/gitlab/codequality/spotbugs/SpotbugsFindingProvider.java

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import jakarta.xml.bind.JAXBException;
1010
import jakarta.xml.bind.Unmarshaller;
1111
import java.io.File;
12+
import java.io.InputStream;
1213
import java.nio.file.Path;
1314
import java.util.List;
1415
import java.util.Optional;
@@ -28,12 +29,18 @@ public SpotbugsFindingProvider(MavenProject project, File repositoryRoot, Logger
2829
}
2930

3031
@Override
31-
public List<Finding> getFindings(File inputFile) {
32+
public String getName() {
33+
return "SpotBugs";
34+
}
35+
36+
@Override
37+
public List<Finding> getFindings(InputStream stream) {
3238

3339
try {
3440

35-
Unmarshaller unmarshaller = JAXBContext.newInstance(BugCollection.class).createUnmarshaller();
36-
BugCollection bugCollection = (BugCollection) unmarshaller.unmarshal(inputFile);
41+
JAXBContext jaxbContext = JAXBContext.newInstance(BugCollection.class);
42+
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
43+
BugCollection bugCollection = (BugCollection) unmarshaller.unmarshal(stream);
3744

3845
return bugCollection.getBugInstance().stream()
3946
.map(this::transformBugInstance)
@@ -51,22 +58,11 @@ private Finding transformBugInstance(BugInstance bugInstance) {
5158
Optional<SourcePosition> sourcePosition = getSourcePosition(bugInstance);
5259

5360
Finding finding = new Finding();
54-
55-
// a short description
56-
finding.setDescription(bugInstance.getShortMessage());
57-
58-
// unique fingerprint
61+
finding.setDescription(String.format("%s: %s", getName(), bugInstance.getShortMessage()));
5962
finding.setFingerprint(bugInstance.getInstanceHash());
60-
61-
// translate priority to severity
6263
finding.setSeverity(getSeverity(bugInstance.getPriority()));
63-
64-
// path of the affected file
6564
finding.setPath(sourcePosition.map(this::getRepositoryRelativePath).orElse("ERROR"));
66-
67-
// source code line
6865
finding.setLine(sourcePosition.map(SourcePosition::getLine).orElse(1));
69-
7066
return finding;
7167

7268
}

src/main/xsd/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
## Spotbugs
44

55
https://raw.githubusercontent.com/spotbugs/spotbugs/master/spotbugs/etc/bugcollection.xsd
6+
7+
## Checkstyle
8+
9+
https://raw.githubusercontent.com/linkedin/pygradle/master/pygradle-plugin/src/test/resources/checkstyle/checkstyle.xsd

0 commit comments

Comments
 (0)