diff --git a/SUPPORTED-FORMATS.md b/SUPPORTED-FORMATS.md
index 7e99e7cfb..e72c9d415 100644
--- a/SUPPORTED-FORMATS.md
+++ b/SUPPORTED-FORMATS.md
@@ -2178,6 +2178,27 @@ analyze - iccxxxxcompiler_opts cstat2.cFor details check the IAR C-
-
+
valgrind
diff --git a/src/main/java/edu/hm/hafner/analysis/parser/ValeParser.java b/src/main/java/edu/hm/hafner/analysis/parser/ValeParser.java
new file mode 100644
index 000000000..f75c5a07a
--- /dev/null
+++ b/src/main/java/edu/hm/hafner/analysis/parser/ValeParser.java
@@ -0,0 +1,75 @@
+package edu.hm.hafner.analysis.parser;
+
+import java.io.Serial;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import edu.hm.hafner.analysis.Issue;
+import edu.hm.hafner.analysis.IssueBuilder;
+import edu.hm.hafner.analysis.Report;
+import edu.hm.hafner.analysis.Severity;
+
+/**
+ * Parser for vale reports.
+ */
+public class ValeParser extends JsonIssueParser {
+ // Constants for JSON keys
+ private static final String CHECK = "Check";
+ private static final String LINE_KEY = "Line";
+ private static final String LINK_KEY = "Link";
+ private static final String MESSAGE_KEY = "Message";
+ private static final String SPAN_KEY = "Span";
+ private static final String SEVERITY_KEY = "Severity";
+
+ @Serial
+ private static final long serialVersionUID = -4034450901865555017L;
+
+ @Override
+ protected void parseJsonObject(final Report report, final JSONObject jsonReport, final IssueBuilder issueBuilder) {
+ JSONArray fileNames = jsonReport.names();
+ for (Object o : fileNames) {
+ if (o instanceof String f) {
+ JSONArray jsonArray = jsonReport.getJSONArray(f);
+ for (Object data : jsonArray) {
+ if (data instanceof JSONObject dataObject) {
+ report.add(createIssue(issueBuilder, f, dataObject));
+ }
+ }
+ }
+ }
+ }
+
+ private Issue createIssue(final IssueBuilder issueBuilder, final String fileName, final JSONObject data) {
+ String checker = data.getString(CHECK);
+ String message = data.getString(MESSAGE_KEY);
+ String severity = data.getString(SEVERITY_KEY);
+ String link = data.getString(LINK_KEY);
+ int line = data.getInt(LINE_KEY);
+ JSONArray span = data.getJSONArray(SPAN_KEY);
+ int startColumn = span.getInt(0);
+ int endColumn = span.getInt(1);
+ final Severity analysisSeverity;
+ switch (severity) {
+ case "error":
+ analysisSeverity = Severity.ERROR;
+ break;
+ case "warning":
+ analysisSeverity = Severity.WARNING_NORMAL;
+ break;
+ case "suggestion":
+ analysisSeverity = Severity.WARNING_LOW;
+ break;
+ default:
+ analysisSeverity = Severity.WARNING_NORMAL;
+ break;
+ }
+ return issueBuilder.setFileName(fileName).setDescription(checker)
+ .setMessage(message).setSeverity(analysisSeverity)
+ .setReference(link)
+ .setLineStart(line)
+ .setLineEnd(line)
+ .setColumnStart(startColumn).setColumnEnd(endColumn)
+ .buildAndClean();
+ }
+}
diff --git a/src/main/java/edu/hm/hafner/analysis/registry/ParserRegistry.java b/src/main/java/edu/hm/hafner/analysis/registry/ParserRegistry.java
index 57bdea582..d09d9854b 100644
--- a/src/main/java/edu/hm/hafner/analysis/registry/ParserRegistry.java
+++ b/src/main/java/edu/hm/hafner/analysis/registry/ParserRegistry.java
@@ -167,6 +167,7 @@ public class ParserRegistry {
new TnsdlDescriptor(),
new TrivyDescriptor(),
new TsLintDescriptor(),
+ new ValeDescriptor(),
new ValgrindDescriptor(),
new VeraCodePipelineScannerDescriptor(),
new XlcDescriptor(),
diff --git a/src/main/java/edu/hm/hafner/analysis/registry/ValeDescriptor.java b/src/main/java/edu/hm/hafner/analysis/registry/ValeDescriptor.java
new file mode 100644
index 000000000..2b120458c
--- /dev/null
+++ b/src/main/java/edu/hm/hafner/analysis/registry/ValeDescriptor.java
@@ -0,0 +1,36 @@
+package edu.hm.hafner.analysis.registry;
+
+import edu.hm.hafner.analysis.IssueParser;
+import edu.hm.hafner.analysis.parser.ValeParser;
+
+/**
+ * Descriptor for the vale prose linter.
+ */
+public class ValeDescriptor extends ParserDescriptor {
+ private static final String ID = "vale";
+ private static final String NAME = "Vale";
+
+ ValeDescriptor() {
+ super(ID, NAME);
+ }
+
+ @Override
+ public IssueParser createParser(Option... options) {
+ return new ValeParser();
+ }
+
+ @Override
+ public String getPattern() {
+ return "**/vale-report.json";
+ }
+
+ @Override
+ public String getUrl() {
+ return "https://vale.sh/";
+ }
+
+ @Override
+ public String getHelp() {
+ return "Reads vale report files. Use the flag --output=JSON";
+ }
+}
diff --git a/src/test/java/edu/hm/hafner/analysis/parser/ValeParserTest.java b/src/test/java/edu/hm/hafner/analysis/parser/ValeParserTest.java
new file mode 100644
index 000000000..b43bbef28
--- /dev/null
+++ b/src/test/java/edu/hm/hafner/analysis/parser/ValeParserTest.java
@@ -0,0 +1,47 @@
+package edu.hm.hafner.analysis.parser;
+
+import edu.hm.hafner.analysis.IssueParser;
+import edu.hm.hafner.analysis.Report;
+import edu.hm.hafner.analysis.Severity;
+import edu.hm.hafner.analysis.assertions.SoftAssertions;
+import edu.hm.hafner.analysis.registry.AbstractParserTest;
+
+class ValeParserTest extends AbstractParserTest {
+ ValeParserTest() {
+ super("vale-report.json");
+ }
+
+ @Override
+ protected void assertThatIssuesArePresent(final Report report, final SoftAssertions softly) {
+ softly.assertThat(report).hasSize(3).hasDuplicatesSize(0);
+ softly.assertThat(report.get(0))
+ .hasFileName("file1.adoc")
+ .hasDescription("RedHat.SentenceLength")
+ .hasMessage("Try to keep sentences to an average of 32 words or fewer.")
+ .hasLineStart(10)
+ .hasColumnStart(1)
+ .hasColumnEnd(5)
+ .hasSeverity(Severity.WARNING_LOW);
+ softly.assertThat(report.get(1))
+ .hasFileName("file2.adoc")
+ .hasDescription("RedHat.TermsWarnings")
+ .hasMessage("Consider using 'might' or 'can' rather than 'may' unless updating existing content that uses the term.")
+ .hasLineStart(39)
+ .hasColumnStart(143)
+ .hasColumnEnd(145)
+ .hasSeverity(Severity.WARNING_NORMAL);
+ softly.assertThat(report.get(2))
+ .hasFileName("file2.adoc")
+ .hasDescription("RedHat.Using")
+ .hasMessage("Use 'by using' instead of 'using' when it follows a noun for clarity and grammatical correctness.")
+ .hasLineStart(51)
+ .hasColumnStart(44)
+ .hasColumnEnd(64)
+ .hasSeverity(Severity.ERROR);
+ }
+
+ @Override
+ protected IssueParser createParser() {
+ return new ValeParser();
+ }
+}
diff --git a/src/test/java/edu/hm/hafner/analysis/registry/ParserRegistryTest.java b/src/test/java/edu/hm/hafner/analysis/registry/ParserRegistryTest.java
index d16108447..24dfcb343 100644
--- a/src/test/java/edu/hm/hafner/analysis/registry/ParserRegistryTest.java
+++ b/src/test/java/edu/hm/hafner/analysis/registry/ParserRegistryTest.java
@@ -21,7 +21,7 @@
class ParserRegistryTest extends ResourceTest {
// Note for parser developers: if you add a new parser,
// please check if you are using the correct type and increment the corresponding count
- private static final long WARNING_PARSERS_COUNT = 127L;
+ private static final long WARNING_PARSERS_COUNT = 128L;
private static final long BUG_PARSERS_COUNT = 3L;
private static final long VULNERABILITY_PARSERS_COUNT = 7L;
private static final long DUPLICATION_PARSERS_COUNT = 3L;
diff --git a/src/test/resources/edu/hm/hafner/analysis/parser/vale-report.json b/src/test/resources/edu/hm/hafner/analysis/parser/vale-report.json
new file mode 100644
index 000000000..2d1a71dc3
--- /dev/null
+++ b/src/test/resources/edu/hm/hafner/analysis/parser/vale-report.json
@@ -0,0 +1,64 @@
+{
+ "file1.adoc": [
+ {
+ "Action": {
+ "Name": "",
+ "Params": null
+ },
+ "Span": [
+ 1,
+ 5
+ ],
+ "Check": "RedHat.SentenceLength",
+ "Description": "",
+ "Link": "https://redhat-documentation.github.io/vale-at-red-hat/docs/main/reference-guide/sentencelength/",
+ "Message": "Try to keep sentences to an average of 32 words or fewer.",
+ "Severity": "suggestion",
+ "Match": "While",
+ "Line": 10
+ }
+ ],
+ "file2.adoc": [
+ {
+ "Action": {
+ "Name": "replace",
+ "Params": [
+ "might",
+ "can"
+ ]
+ },
+ "Span": [
+ 143,
+ 145
+ ],
+ "Check": "RedHat.TermsWarnings",
+ "Description": "",
+ "Link": "https://redhat-documentation.github.io/vale-at-red-hat/docs/main/reference-guide/termswarnings/",
+ "Message": "Consider using 'might' or 'can' rather than 'may' unless updating existing content that uses the term.",
+ "Severity": "warning",
+ "Match": "may",
+ "Line": 39
+ },
+ {
+ "Action": {
+ "Name": "edit",
+ "Params": [
+ "regex",
+ "(\\w+)( using)",
+ "$1 by using"
+ ]
+ },
+ "Span": [
+ 44,
+ 64
+ ],
+ "Check": "RedHat.Using",
+ "Description": "",
+ "Link": "https://redhat-documentation.github.io/vale-at-red-hat/docs/main/reference-guide/using/",
+ "Message": "Use 'by using' instead of 'using' when it follows a noun for clarity and grammatical correctness.",
+ "Severity": "error",
+ "Match": "functionalities using",
+ "Line": 51
+ }
+ ]
+}
\ No newline at end of file
|