From b2d534249379a93fe5e20c978a86672036d72261 Mon Sep 17 00:00:00 2001 From: "Bob W. Hogg" Date: Sat, 2 Jan 2016 12:30:49 -0800 Subject: [PATCH] Adds new Code Climate-compliant JSON renderer --- pmd-core/pom.xml | 6 ++ .../pmd/renderers/CodeClimateIssue.java | 55 ++++++++++++++ .../pmd/renderers/CodeClimateRenderer.java | 72 +++++++++++++++++++ .../pmd/renderers/RendererFactory.java | 1 + .../renderers/CodeClimateRendererTest.java | 31 ++++++++ 5 files changed, 165 insertions(+) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateIssue.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java create mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index 05d4226d58..694f59885f 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -93,6 +93,12 @@ org.ow2.asm asm + + com.google.code.gson + gson + 2.5 + + net.sourceforge.saxon diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateIssue.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateIssue.java new file mode 100644 index 0000000000..658882c8f8 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateIssue.java @@ -0,0 +1,55 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.renderers; + +import net.sourceforge.pmd.*; + +/** + * Structure for the Code Climate Issue spec (https://github.com/codeclimate/spec/blob/master/SPEC.md#issues) + */ +public class CodeClimateIssue { + public final String type = "issue"; + public String check_name; + public String description; + public Content content; + public final String[] categories = { "Style" }; + public Location location; + public String severity; + + /** + * Location structure + */ + public static class Location { + public String path; + public Lines lines; + + private class Lines { + public int begin; + public int end; + } + + public Location(String path, int beginLine, int endLine) { + this.path = path; + this.lines = new Lines(); + lines.begin = beginLine; + lines.end = endLine; + } + } + + /** + * Content structure + */ + public static class Content { + public String body; + + /** + * Strip out all newlines from the body + * @param {String} body The text to compose the content from + */ + public Content(String body) { + this.body = body.replace(PMD.EOL, " "); + } + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java new file mode 100644 index 0000000000..1687fc4b01 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java @@ -0,0 +1,72 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.renderers; + +import com.google.gson.Gson; +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.RuleViolation; + +import java.io.IOException; +import java.io.Writer; +import java.util.Iterator; + +/** + * Renderer for Code Climate JSON format + */ +public class CodeClimateRenderer extends AbstractIncrementingRenderer { + public static final String NAME = "codeclimate"; + + protected static final String EOL = System.getProperty("line.separator", "\n"); + + public CodeClimateRenderer() { + super(NAME, "Code Climate integration."); + } + + /** + * {@inheritDoc} + */ + @Override + public void renderFileViolations(Iterator violations) throws IOException { + Writer writer = getWriter(); + Gson gson = new Gson(); + while (violations.hasNext()) { + RuleViolation rv = violations.next(); + writer.write(gson.toJson(makeIssue(rv)) + EOL); + } + } + + /** + * Generate a CodeClimateIssue suitable for processing into JSON from the given RuleViolation. + * @param rv RuleViolation to convert. + * @return The generated issue. + */ + private CodeClimateIssue makeIssue(RuleViolation rv) { + CodeClimateIssue issue = new CodeClimateIssue(); + Rule rule = rv.getRule(); + issue.check_name = rule.getName(); + issue.description = rv.getDescription(); + issue.content = new CodeClimateIssue.Content(rule.getDescription()); + issue.location = new CodeClimateIssue.Location(rv.getFilename(), rv.getBeginLine(), rv.getEndLine()); + switch(rule.getPriority()) { + case HIGH: + issue.severity = "critical"; + break; + case MEDIUM_HIGH: + case MEDIUM: + case MEDIUM_LOW: + issue.severity = "normal"; + break; + case LOW: + issue.severity = "info"; + break; + } + return issue; + } + + @Override + public String defaultFileExtension() { + return "json"; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java index 132c0051b9..dfc09214e2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java @@ -26,6 +26,7 @@ public class RendererFactory { public static final Map> REPORT_FORMAT_TO_RENDERER; static { Map> map = new TreeMap>(); + map.put(CodeClimateRenderer.NAME, CodeClimateRenderer.class); map.put(XMLRenderer.NAME, XMLRenderer.class); map.put(IDEAJRenderer.NAME, IDEAJRenderer.class); map.put(TextColorRenderer.NAME, TextColorRenderer.class); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java new file mode 100644 index 0000000000..1572f9f43d --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java @@ -0,0 +1,31 @@ +package net.sourceforge.pmd.renderers; + +import net.sourceforge.pmd.*; + +public class CodeClimateRendererTest extends AbstractRendererTst { + + @Override + public Renderer getRenderer() { + return new CodeClimateRenderer(); + } + + @Override + public String getExpected() { + return "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\",\"content\":{\"body\":\"desc\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"n/a\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\"}" + PMD.EOL; + } + + @Override + public String getExpectedEmpty() { + return ""; + } + + @Override + public String getExpectedMultiple() { + return "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\",\"content\":{\"body\":\"desc\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"n/a\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\"}" + PMD.EOL + + "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\",\"content\":{\"body\":\"desc\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"n/a\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\"}" + PMD.EOL; + } + + public static junit.framework.Test suite() { + return new junit.framework.JUnit4TestAdapter(CodeClimateRendererTest.class); + } +}