diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java
deleted file mode 100644
index f7d5500f..00000000
--- a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube.
- * Copyright © 2012 OCTO Technology, Backelite (${email})
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-package org.sonar.plugins.objectivec.violations.oclint;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.xml.stream.XMLStreamException;
-
-import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.SensorContext;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.resources.Project;
-import org.sonar.api.utils.StaxParser;
-
-final class OCLintParser {
-
- private final Project project;
- private final SensorContext context;
- private final ResourcePerspectives resourcePerspectives;
- private final FileSystem fileSystem;
-
- public OCLintParser(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives, final FileSystem fileSystem) {
- project = p;
- context = c;
- this.resourcePerspectives = resourcePerspectives;
- this.fileSystem = fileSystem;
- }
-
- public void parseReport(final File file) {
-
- try {
- final InputStream reportStream = new FileInputStream(file);
- parseReport(reportStream);
- reportStream.close();
- } catch (final IOException e) {
- LoggerFactory.getLogger(getClass()).error("Error processing file named {}", file, e);
- }
-
- }
-
- public void parseReport(final InputStream inputStream) {
-
- try {
- final StaxParser parser = new StaxParser(
- new OCLintXMLStreamHandler(project, context, resourcePerspectives, fileSystem));
- parser.parse(inputStream);
- } catch (final XMLStreamException e) {
- LoggerFactory.getLogger(getClass()).error(
- "Error while parsing XML stream.", e);
- }
- }
-
-}
diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintReportParser.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintReportParser.java
new file mode 100644
index 00000000..c4dfdefb
--- /dev/null
+++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintReportParser.java
@@ -0,0 +1,104 @@
+/**
+ * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube.
+ * Copyright © 2012 OCTO Technology, Backelite (${email})
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+package org.sonar.plugins.objectivec.violations.oclint;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.annotation.Nonnull;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+final class OCLintReportParser {
+ private static final Logger LOGGER = LoggerFactory.getLogger(OCLintReportParser.class);
+
+ private static final String VIOLATION = "violation";
+ private static final String PATH = "path";
+ private static final String START_LINE = "startline";
+ private static final String RULE = "rule";
+ private static final String MESSAGE = "message";
+
+ @Nonnull
+ private static NodeList getViolationElements(@Nonnull Document document) {
+ return document.getElementsByTagName(VIOLATION);
+ }
+
+ @Nonnull
+ List parse(@Nonnull final File xmlFile) {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+
+ try {
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(xmlFile);
+
+ return parse(document);
+ } catch (final FileNotFoundException e){
+ LOGGER.error("OCLint report not found {}", xmlFile, e);
+ } catch (final ParserConfigurationException e) {
+ LOGGER.error("Error parsing file named {}", xmlFile, e);
+ } catch (final IOException | SAXException e) {
+ LOGGER.error("Error processing file named {}", xmlFile, e);
+ }
+
+ return Collections.emptyList();
+ }
+
+ @Nonnull
+ List parse(@Nonnull final Document document) {
+ List violations = new ArrayList<>();
+
+ NodeList violationElements = getViolationElements(document);
+ for (int i = 0; i < violationElements.getLength(); i++) {
+ Node node = violationElements.item(i);
+ if (isNotElement(node)) {
+ continue;
+ }
+
+ Element element = (Element) node;
+ violations.add(buildViolation(element));
+ }
+
+ return violations;
+ }
+
+ private boolean isNotElement(@Nonnull Node item) {
+ return item.getNodeType() != Node.ELEMENT_NODE;
+ }
+
+ @Nonnull
+ private Violation buildViolation(@Nonnull Element element) {
+ return Violation.builder()
+ .setPath(element.getAttribute(PATH))
+ .setStartLine(Integer.parseInt(element.getAttribute(START_LINE)))
+ .setRule(element.getAttribute(RULE))
+ .setMessage(element.getAttribute(MESSAGE))
+ .build();
+ }
+}
diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java
index f9cd192c..6499e889 100644
--- a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java
+++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java
@@ -26,6 +26,7 @@
import org.sonar.plugins.objectivec.core.ObjectiveC;
import org.sonar.squidbridge.rules.SqaleXmlLoader;
+import javax.annotation.Nonnull;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -47,7 +48,7 @@ public class OCLintRulesDefinition implements RulesDefinition {
private static final String RULES_FILE = "/org/sonar/plugins/oclint/rules.txt";
@Override
- public void define(Context context) {
+ public void define(@Nonnull Context context) {
NewRepository repository = context
.createRepository(REPOSITORY_KEY, ObjectiveC.KEY)
@@ -73,7 +74,7 @@ private void loadRules(NewRepository repository) throws IOException {
final List listLines = IOUtils.readLines(reader);
String previousLine = null;
- Map rule = new HashMap();
+ Map rule = new HashMap<>();
boolean inDescription = false;
for (String line : listLines) {
@@ -86,7 +87,7 @@ private void loadRules(NewRepository repository) throws IOException {
// Remove the rule name from the description of the previous
// rule
if (rule.get("description") != null) {
- String description = rule.get("description").toString();
+ String description = rule.get("description");
final int index = description.lastIndexOf(previousLine);
if (index > 0) {
rule.put("description", description.substring(0, index));
@@ -107,10 +108,10 @@ private void loadRules(NewRepository repository) throws IOException {
inDescription = true;
// Create rule when last filed found
- RulesDefinition.NewRule newRule = repository.createRule(rule.get("key").toString());
- newRule.setName(rule.get("name").toString());
- newRule.setSeverity(rule.get("severity").toString());
- newRule.setHtmlDescription(rule.get("description").toString());
+ RulesDefinition.NewRule newRule = repository.createRule(rule.get("key"));
+ newRule.setName(rule.get("name"));
+ newRule.setSeverity(rule.get("severity"));
+ newRule.setHtmlDescription(rule.get("description"));
} else if (line.matches("Severity:.*")) {
inDescription = false;
@@ -119,7 +120,7 @@ private void loadRules(NewRepository repository) throws IOException {
} else {
if (inDescription) {
line = ruleDescriptionLink(line);
- String description = (String)rule.get("description");
+ String description = rule.get("description");
rule.put("description", description + "
" + line);
}
}
diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java
index 13bb0af9..7333d79f 100644
--- a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java
+++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java
@@ -18,66 +18,77 @@
package org.sonar.plugins.objectivec.violations.oclint;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.tools.ant.DirectoryScanner;
+import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.Sensor;
-import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.config.Settings;
-import org.sonar.api.resources.Project;
import org.sonar.plugins.objectivec.ObjectiveCPlugin;
import org.sonar.plugins.objectivec.core.ObjectiveC;
+import javax.annotation.Nonnull;
+
public final class OCLintSensor implements Sensor {
public static final String REPORT_PATH_KEY = ObjectiveCPlugin.PROPERTY_PREFIX + ".oclint.report";
public static final String DEFAULT_REPORT_PATH = "sonar-reports/*oclint.xml";
+ private static final Logger LOGGER = LoggerFactory.getLogger(OCLintSensor.class);
+ private static final String NAME = "OCLint violation sensor";
+
+ private final OCLintReportParser parser = new OCLintReportParser();
private final Settings conf;
private final FileSystem fileSystem;
- private final ResourcePerspectives resourcePerspectives;
- public OCLintSensor(final FileSystem fileSystem, final Settings config, final ResourcePerspectives resourcePerspectives) {
+ public OCLintSensor(final FileSystem fileSystem, final Settings config) {
this.conf = config;
this.fileSystem = fileSystem;
- this.resourcePerspectives = resourcePerspectives;
}
- public boolean shouldExecuteOnProject(final Project project) {
-
- return project.isRoot() && fileSystem.languages().contains(ObjectiveC.KEY);
-
+ @Override
+ public void describe(@Nonnull SensorDescriptor descriptor) {
+ descriptor.name(NAME);
+ descriptor.onlyOnLanguage(ObjectiveC.KEY);
}
- public void analyse(final Project project, final SensorContext context) {
+ @Override
+ public void execute(@Nonnull org.sonar.api.batch.sensor.SensorContext context) {
final String projectBaseDir = fileSystem.baseDir().getPath();
- final OCLintParser parser = new OCLintParser(project, context, resourcePerspectives, fileSystem);
-
- parseReportIn(projectBaseDir, parser);
+ parseReportIn(projectBaseDir, OCLintViolationPersistor.create(context));
}
- private void parseReportIn(final String baseDir, final OCLintParser parser) {
-
+ private void parseReportIn(final String baseDir, OCLintViolationPersistor persistor) {
DirectoryScanner scanner = new DirectoryScanner();
- scanner.setIncludes(new String[]{reportPath()});
+ scanner.setIncludes(new String[]{buildReportPath()});
scanner.setBasedir(baseDir);
scanner.setCaseSensitive(false);
scanner.scan();
String[] files = scanner.getIncludedFiles();
+ List violations = new ArrayList<>();
+
for(String filename : files) {
- LoggerFactory.getLogger(getClass()).info("Processing OCLint report {}", filename);
- parser.parseReport(new File(filename));
+ LOGGER.info("Processing OCLint report {}", filename);
+
+ violations.addAll(parser.parse(new File(filename)));
}
+
+ persistor.saveViolations(violations);
}
- private String reportPath() {
+ private String buildReportPath() {
String reportPath = conf.getString(REPORT_PATH_KEY);
+
if (reportPath == null) {
+ LOGGER.debug("No value specified for \"" + REPORT_PATH_KEY + "\" using default path");
reportPath = DEFAULT_REPORT_PATH;
}
+
return reportPath;
}
}
diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintViolationPersistor.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintViolationPersistor.java
new file mode 100644
index 00000000..d95aa49f
--- /dev/null
+++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintViolationPersistor.java
@@ -0,0 +1,79 @@
+/**
+ * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube.
+ * Copyright © 2012 OCTO Technology, Backelite (${email})
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+package org.sonar.plugins.objectivec.violations.oclint;
+
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.issue.NewIssue;
+import org.sonar.api.batch.sensor.issue.NewIssueLocation;
+import org.sonar.api.rule.RuleKey;
+
+import javax.annotation.Nonnull;
+import java.io.File;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+final class OCLintViolationPersistor {
+ private final SensorContext context;
+ private final FileSystem fileSystem;
+
+ private OCLintViolationPersistor(@Nonnull final SensorContext context, @Nonnull final FileSystem fileSystem) {
+ this.context = context;
+ this.fileSystem = fileSystem;
+ }
+
+ @Nonnull
+ static OCLintViolationPersistor create(@Nonnull final SensorContext context) {
+ return new OCLintViolationPersistor(context, context.fileSystem());
+ }
+
+ void saveViolations(@Nonnull List violations) {
+ violations.stream()
+ .collect(Collectors.groupingBy(Violation::getPath))
+ .forEach(this::saveViolationsGroupedByFile);
+ }
+
+ private void saveViolationsGroupedByFile(@Nonnull String absoluteFilePath, @Nonnull List violations) {
+ Optional value = buildInputFile(absoluteFilePath);
+ value.ifPresent(inputFile -> {
+ for (Violation violation : violations) {
+ RuleKey rule = RuleKey.of(OCLintRulesDefinition.REPOSITORY_KEY, violation.getRule());
+ NewIssue newIssue = context.newIssue().forRule(rule);
+
+ NewIssueLocation location = newIssue.newLocation()
+ .on(inputFile)
+ .at(inputFile.selectLine(violation.getStartLine()))
+ .message(violation.getMessage());
+
+ newIssue.at(location)
+ .save();
+ }
+ });
+ }
+
+ @Nonnull
+ private Optional buildInputFile(@Nonnull String absoluteFilePath) {
+ File file = new File(absoluteFilePath);
+ FilePredicate predicate = fileSystem.predicates().hasAbsolutePath(file.getAbsolutePath());
+
+ return Optional.ofNullable(fileSystem.inputFile(predicate));
+ }
+}
diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java
deleted file mode 100644
index 28c51030..00000000
--- a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube.
- * Copyright © 2012 OCTO Technology, Backelite (${email})
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-package org.sonar.plugins.objectivec.violations.oclint;
-
-import java.io.File;
-
-import javax.xml.stream.XMLStreamException;
-
-import org.codehaus.staxmate.in.SMHierarchicCursor;
-import org.codehaus.staxmate.in.SMInputCursor;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.SensorContext;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.issue.Issuable;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.resources.Project;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.utils.StaxParser.XmlStreamHandler;
-
-final class OCLintXMLStreamHandler implements XmlStreamHandler {
- private static final int PMD_MINIMUM_PRIORITY = 5;
- private final Project project;
- private final SensorContext context;
- private final ResourcePerspectives resourcePerspectives;
- private final FileSystem fileSystem;
-
- public OCLintXMLStreamHandler(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives, final FileSystem fileSystem) {
- project = p;
- context = c;
- this.resourcePerspectives = resourcePerspectives;
- this.fileSystem = fileSystem;
- }
-
- public void stream(final SMHierarchicCursor rootCursor) throws XMLStreamException {
-
- final SMInputCursor file = rootCursor.advance().childElementCursor("file");
- while (null != file.getNext()) {
- collectIssuesFor(file);
- }
- }
-
- private void collectIssuesFor(final SMInputCursor file) throws XMLStreamException {
-
- final String filePath = file.getAttrValue("name");
- LoggerFactory.getLogger(getClass()).debug("Collection violations for {}", filePath);
- final InputFile inputFile = findResource(filePath);
- if (fileExists(inputFile)) {
- LoggerFactory.getLogger(getClass()).debug("File {} was found in the project.", filePath);
- collectFileIssues(inputFile, file);
- }
- }
-
- private void collectFileIssues(final InputFile inputFile, final SMInputCursor file) throws XMLStreamException {
-
- final SMInputCursor line = file.childElementCursor("violation");
-
- while (null != line.getNext()) {
- recordViolation(inputFile, line);
- }
- }
-
- private InputFile findResource(final String filePath) {
-
- File file = new File(filePath);
- return fileSystem.inputFile(fileSystem.predicates().hasAbsolutePath(file.getAbsolutePath()));
-
- }
-
- private void recordViolation(InputFile inputFile, final SMInputCursor line) throws XMLStreamException {
-
- Issuable issuable = resourcePerspectives.as(Issuable.class, inputFile);
-
- if (issuable != null) {
-
- Issue issue = issuable.newIssueBuilder()
- .ruleKey(RuleKey.of(OCLintRulesDefinition.REPOSITORY_KEY, line.getAttrValue("rule")))
- .line(Integer.valueOf(line.getAttrValue("beginline")))
- .message(line.getElemStringValue())
- .build();
-
- issuable.addIssue(issue);
-
-
- }
- }
-
- private boolean fileExists(InputFile file) {
- if (file == null) {
- return false;
- }
-
- return context.getResource(file) != null;
- }
-
-}
diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/Violation.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/Violation.java
new file mode 100644
index 00000000..544e649a
--- /dev/null
+++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/Violation.java
@@ -0,0 +1,125 @@
+/**
+ * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube.
+ * Copyright © 2012 OCTO Technology, Backelite (${email})
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+package org.sonar.plugins.objectivec.violations.oclint;
+
+import javax.annotation.Nonnull;
+import java.util.Objects;
+
+final class Violation {
+ private final String path;
+ private final int startLine;
+ private final String rule;
+ private final String message;
+
+ Violation(Builder builder) {
+ this.path = builder.path;
+ this.startLine = builder.startLine;
+ this.rule = builder.rule;
+ this.message = builder.message;
+ }
+
+ @Nonnull
+ public String getPath() {
+ return path;
+ }
+
+ public int getStartLine() {
+ return startLine;
+ }
+
+ @Nonnull
+ public String getRule() {
+ return rule;
+ }
+
+ @Nonnull
+ public String getMessage() {
+ return message;
+ }
+
+ @Nonnull
+ static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof Violation)) {
+ return false;
+ }
+
+ Violation violation = (Violation) o;
+ return startLine == violation.startLine &&
+ Objects.equals(path, violation.path) &&
+ Objects.equals(rule, violation.rule) &&
+ Objects.equals(message, violation.message);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ path,
+ startLine,
+ rule,
+ message
+ );
+ }
+
+ static class Builder {
+ private String path = "";
+ private int startLine;
+ private String rule = "";
+ private String message = "";
+
+ private Builder() {
+ }
+
+ @Nonnull
+ Builder setPath(@Nonnull String path) {
+ this.path = path;
+ return this;
+ }
+
+ @Nonnull
+ Builder setStartLine(int startLine) {
+ this.startLine = startLine;
+ return this;
+ }
+
+ @Nonnull
+ Builder setRule(@Nonnull String rule) {
+ this.rule = rule;
+ return this;
+ }
+
+ @Nonnull
+ Builder setMessage(@Nonnull String message) {
+ this.message = message;
+ return this;
+ }
+
+ @Nonnull
+ Violation build() {
+ return new Violation(this);
+ }
+ }
+}
diff --git a/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintReportParserTest.java b/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintReportParserTest.java
new file mode 100644
index 00000000..2c992a34
--- /dev/null
+++ b/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintReportParserTest.java
@@ -0,0 +1,105 @@
+/**
+ * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube.
+ * Copyright © 2012 OCTO Technology, Backelite (${email})
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+package org.sonar.plugins.objectivec.violations.oclint;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import javax.annotation.Nonnull;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(JUnit4.class)
+public class OCLintReportParserTest {
+ private final Path resourcePath = Paths.get("src", "test", "resources", "oclint");
+
+ private DocumentBuilder builder;
+ private OCLintReportParser parser;
+
+ @Before
+ public void setUp() throws Exception {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ builder = factory.newDocumentBuilder();
+
+ parser = new OCLintReportParser();
+ }
+
+ @Test
+ public void parse_withEmptyDocument() throws Exception {
+ Path documentPath = Paths.get(resourcePath.toString(), "empty.xml");
+ Document document = builder.parse(documentPath.toFile());
+
+ List actual = parser.parse(document);
+
+ assertTrue(actual.isEmpty());
+ }
+
+ @Test
+ public void parse_withSampleDocument() throws IOException, SAXException {
+ Path documentPath = Paths.get(resourcePath.toString(), "oclint.xml");
+ Document document = builder.parse(documentPath.toFile());
+ List expected = buildExpectedViolationsForSample();
+
+ List actual = parser.parse(document);
+
+ assertEquals(expected, actual);
+ }
+
+ @Nonnull
+ private List buildExpectedViolationsForSample() {
+ List expected = new ArrayList<>();
+ Violation violation;
+
+ violation = Violation.builder()
+ .setPath("RASqlite/RASqlite.m")
+ .setStartLine(281)
+ .setRule("deep nested block")
+ .setMessage("Block depth of 6 exceeds limit of 5")
+ .build();
+ expected.add(violation);
+
+ violation = Violation.builder()
+ .setPath("RASqlite/RASqlite.m")
+ .setStartLine(305)
+ .setRule("ivar assignment outside accessors or init")
+ .build();
+ expected.add(violation);
+
+ violation = Violation.builder()
+ .setPath("RASqlite/RASqlite.m")
+ .setStartLine(707)
+ .setRule("unused method parameter")
+ .setMessage("The parameter 'commit' is unused.")
+ .build();
+ expected.add(violation);
+
+ return expected;
+ }
+}
diff --git a/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java b/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java
new file mode 100644
index 00000000..cff50db0
--- /dev/null
+++ b/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java
@@ -0,0 +1,64 @@
+/**
+ * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube.
+ * Copyright © 2012 OCTO Technology, Backelite (${email})
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+package org.sonar.plugins.objectivec.violations.oclint;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
+import org.sonar.api.config.MapSettings;
+import org.sonar.api.config.Settings;
+import org.sonar.plugins.objectivec.core.ObjectiveC;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(JUnit4.class)
+public class OCLintSensorTest {
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private OCLintSensor sensor;
+
+ @Before
+ public void prepare() throws IOException {
+ File baseDirectory = temporaryFolder.newFolder();
+
+ DefaultFileSystem fileSystem = new DefaultFileSystem(baseDirectory.toPath());
+ Settings settings = new MapSettings();
+
+ sensor = new OCLintSensor(fileSystem, settings);
+ }
+
+ @Test
+ public void describe() {
+ DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor();
+
+ sensor.describe(descriptor);
+
+ assertEquals("OCLint violation sensor", descriptor.name());
+ assertTrue(descriptor.languages().contains(ObjectiveC.KEY));
+ }
+}
diff --git a/sonar-objective-c-plugin/src/test/resources/oclint/empty.xml b/sonar-objective-c-plugin/src/test/resources/oclint/empty.xml
new file mode 100644
index 00000000..04b5dc0d
--- /dev/null
+++ b/sonar-objective-c-plugin/src/test/resources/oclint/empty.xml
@@ -0,0 +1,15 @@
+
+
+ 2018-02-10T08:50:19Z
+
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+
+
+
diff --git a/sonar-objective-c-plugin/src/test/resources/oclint/oclint.xml b/sonar-objective-c-plugin/src/test/resources/oclint/oclint.xml
new file mode 100644
index 00000000..1a2bbdb9
--- /dev/null
+++ b/sonar-objective-c-plugin/src/test/resources/oclint/oclint.xml
@@ -0,0 +1,21 @@
+
+
+ 2018-02-10T08:50:19Z
+
+ 9
+ 6
+ 0
+ 1
+ 2
+
+
+
+
+
+
+
+
+
+
+
+