]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6052 Extract primaryLocation as a special attribute 450/head
authorJulien HENRY <julien.henry@sonarsource.com>
Wed, 29 Jul 2015 15:42:20 +0000 (17:42 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Fri, 31 Jul 2015 09:00:25 +0000 (11:00 +0200)
Also rework new issue API on batch side.

66 files changed:
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/checks/TemplateRuleCheck.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/MultilineIssuesSensor.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/RandomAccessSensor.java
sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchReport.java
sonar-batch-protocol/src/main/protobuf/batch_report.proto
sonar-batch/src/main/java/org/sonar/batch/index/BatchComponent.java
sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java
sonar-batch/src/main/java/org/sonar/batch/issue/DeprecatedIssueBuilderWrapper.java
sonar-batch/src/main/java/org/sonar/batch/issue/DeprecatedIssueWrapper.java
sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java
sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java
sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueRepository.java
sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
sonar-batch/src/main/java/org/sonar/batch/postjob/DefaultPostJobContext.java
sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java
sonar-batch/src/main/java/org/sonar/batch/report/CoveragePublisher.java
sonar-batch/src/main/java/org/sonar/batch/report/SourcePublisher.java
sonar-batch/src/main/java/org/sonar/batch/report/TestExecutionAndCoveragePublisher.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java
sonar-batch/src/main/java/org/sonar/batch/scan/report/SourceProvider.java
sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java
sonar-batch/src/main/java/org/sonar/batch/source/SymbolizableBuilder.java
sonar-batch/src/main/java/org/sonar/batch/test/TestPlanBuilder.java
sonar-batch/src/main/java/org/sonar/batch/test/TestableBuilder.java
sonar-batch/src/test/java/org/sonar/batch/issue/IssueFiltersTest.java
sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java
sonar-batch/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java
sonar-batch/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java
sonar-batch/src/test/java/org/sonar/batch/report/DuplicationsPublisherTest.java
sonar-batch/src/test/java/org/sonar/batch/report/SourcePublisherTest.java
sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
sonar-batch/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java
sonar-batch/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java
sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/HelloJava.xoo [deleted file]
sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/HelloJava.xoo.measures [deleted file]
sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/Multiline.xoo [new file with mode: 0644]
sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/Multiple.xoo [new file with mode: 0644]
sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/Single.xoo [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputComponent.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputComponent.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/postjob/issue/Issue.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/Issue.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/IssueLocation.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssue.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssueLocation.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueLocation.java
sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueLocationTest.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java

index c6fe84360c3e3ed8fd23dba8527748e95fbefa72..fc361d40d7c4e98356a859460d24f3a900592f04 100644 (file)
@@ -40,8 +40,8 @@ public class TemplateRuleCheck implements Check {
     NewIssue newIssue = sensorContext.newIssue();
     newIssue
       .forRule(ruleKey)
-      .addLocation(newIssue.newLocation()
-        .onFile(file)
+      .at(newIssue.newLocation()
+        .on(file)
         .at(file.selectLine(line)))
       .save();
   }
index 47e8a16794df05da70451c9c80ae8b5c24796ee5..e92e5c1256432b0fc2ba56b14f862762e0cecbf8 100644 (file)
@@ -58,8 +58,8 @@ public class CreateIssueByInternalKeySensor implements Sensor {
       NewIssue newIssue = context.newIssue();
       newIssue
         .forRule(rule.ruleKey())
-        .addLocation(newIssue.newLocation()
-          .onFile(file)
+        .at(newIssue.newLocation()
+          .on(file)
           .message("This issue is generated on each file"))
         .save();
     }
index a4bcca64eab8dde787e4867f145470e3af303b5a..57dbc84464be08d6e9f50ab39974ad4670d41679 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.api.batch.sensor.Sensor;
 import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.batch.sensor.SensorDescriptor;
 import org.sonar.api.batch.sensor.issue.NewIssue;
+import org.sonar.api.batch.sensor.issue.NewIssueLocation;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.xoo.Xoo;
 
@@ -76,7 +77,7 @@ public class MultilineIssuesSensor implements Sensor {
         while (m.find()) {
           Integer issueId = Integer.parseInt(m.group(1));
           Integer issueLocationId = Integer.parseInt(m.group(2));
-          TextPointer newPointer = file.newPointer(currentLine, m.start());
+          TextPointer newPointer = file.newPointer(currentLine, m.end());
           if (!startPositions.containsKey(issueId)) {
             startPositions.put(issueId, new HashMap<Integer, TextPointer>());
           }
@@ -100,10 +101,14 @@ public class MultilineIssuesSensor implements Sensor {
     for (Map.Entry<Integer, Map<Integer, TextPointer>> entry : startPositions.entrySet()) {
       NewIssue newIssue = context.newIssue().forRule(ruleKey);
       for (Map.Entry<Integer, TextPointer> location : entry.getValue().entrySet()) {
-        newIssue.addLocation(newIssue.newLocation()
-          .onFile(file)
-          .at(file.newRange(location.getValue(), endPositions.get(entry.getKey()).get(location.getKey())))
-          .message("Multiline issue"));
+        NewIssueLocation newLocation = newIssue.newLocation()
+          .on(file)
+          .at(file.newRange(location.getValue(), endPositions.get(entry.getKey()).get(location.getKey())));
+        if (location.getKey() == 1) {
+          newIssue.at(newLocation.message("Primary location"));
+        } else {
+          newIssue.addLocation(newLocation.message("Location #" + location.getKey()));
+        }
       }
       newIssue.save();
     }
index e53f0b17ccc4d398718bece4cf4e3268524910ba..b2d047a99940229473372194f35c3fbddbf26a6e 100644 (file)
@@ -54,8 +54,8 @@ public class OneIssueOnDirPerFileSensor implements Sensor {
       NewIssue newIssue = context.newIssue();
       newIssue
         .forRule(ruleKey)
-        .addLocation(newIssue.newLocation()
-          .onDir(inputDir)
+        .at(newIssue.newLocation()
+          .on(inputDir)
           .message("This issue is generated for file " + file.relativePath()))
         .save();
     }
index e8aed769087359a99b37200a206c6497c5d2efb8..8ef94f3d99f1a9b4b32a6db2a0274982ab7d6dc9 100644 (file)
@@ -61,8 +61,8 @@ public class OneIssuePerLineSensor implements Sensor {
       NewIssue newIssue = context.newIssue();
       newIssue
         .forRule(ruleKey)
-        .addLocation(newIssue.newLocation()
-          .onFile(file)
+        .at(newIssue.newLocation()
+          .on(file)
           .at(file.selectLine(line))
           .message("This issue is generated on each line"))
         .effortToFix(context.settings().getDouble(EFFORT_TO_FIX_PROPERTY))
index 69bf700b50b445870222f3be9758610c38062ad6..bc7b90a69ae06f55624d272daf94b71c9891b127 100644 (file)
@@ -66,8 +66,8 @@ public class RandomAccessSensor implements Sensor {
     NewIssue newIssue = context.newIssue();
     newIssue
       .forRule(ruleKey)
-      .addLocation(newIssue.newLocation()
-        .onFile(file)
+      .at(newIssue.newLocation()
+        .on(file)
         .at(file.selectLine(1))
         .message("This issue is generated on each file"))
       .save();
index 32168a1f7f8bd9f50e470f56c016a479cace1457..753dea410c22e6c3825564e2be5031c322aa5199 100644 (file)
@@ -6576,51 +6576,64 @@ public final class BatchReport {
         getAttributesBytes();
 
     /**
-     * <code>repeated .IssueLocation locations = 8;</code>
+     * <code>optional .IssueLocation primary_location = 9;</code>
+     */
+    boolean hasPrimaryLocation();
+    /**
+     * <code>optional .IssueLocation primary_location = 9;</code>
+     */
+    org.sonar.batch.protocol.output.BatchReport.IssueLocation getPrimaryLocation();
+    /**
+     * <code>optional .IssueLocation primary_location = 9;</code>
+     */
+    org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getPrimaryLocationOrBuilder();
+
+    /**
+     * <code>repeated .IssueLocation additional_location = 10;</code>
      */
     java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> 
-        getLocationsList();
+        getAdditionalLocationList();
     /**
-     * <code>repeated .IssueLocation locations = 8;</code>
+     * <code>repeated .IssueLocation additional_location = 10;</code>
      */
-    org.sonar.batch.protocol.output.BatchReport.IssueLocation getLocations(int index);
+    org.sonar.batch.protocol.output.BatchReport.IssueLocation getAdditionalLocation(int index);
     /**
-     * <code>repeated .IssueLocation locations = 8;</code>
+     * <code>repeated .IssueLocation additional_location = 10;</code>
      */
-    int getLocationsCount();
+    int getAdditionalLocationCount();
     /**
-     * <code>repeated .IssueLocation locations = 8;</code>
+     * <code>repeated .IssueLocation additional_location = 10;</code>
      */
     java.util.List<? extends org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> 
-        getLocationsOrBuilderList();
+        getAdditionalLocationOrBuilderList();
     /**
-     * <code>repeated .IssueLocation locations = 8;</code>
+     * <code>repeated .IssueLocation additional_location = 10;</code>
      */
-    org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getLocationsOrBuilder(
+    org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getAdditionalLocationOrBuilder(
         int index);
 
     /**
-     * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+     * <code>repeated .ExecutionFlow execution_flow = 11;</code>
      */
     java.util.List<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow> 
-        getExecutionFlowsList();
+        getExecutionFlowList();
     /**
-     * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+     * <code>repeated .ExecutionFlow execution_flow = 11;</code>
      */
-    org.sonar.batch.protocol.output.BatchReport.ExecutionFlow getExecutionFlows(int index);
+    org.sonar.batch.protocol.output.BatchReport.ExecutionFlow getExecutionFlow(int index);
     /**
-     * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+     * <code>repeated .ExecutionFlow execution_flow = 11;</code>
      */
-    int getExecutionFlowsCount();
+    int getExecutionFlowCount();
     /**
-     * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+     * <code>repeated .ExecutionFlow execution_flow = 11;</code>
      */
     java.util.List<? extends org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder> 
-        getExecutionFlowsOrBuilderList();
+        getExecutionFlowOrBuilderList();
     /**
-     * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+     * <code>repeated .ExecutionFlow execution_flow = 11;</code>
      */
-    org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder getExecutionFlowsOrBuilder(
+    org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder getExecutionFlowOrBuilder(
         int index);
   }
   /**
@@ -6720,20 +6733,33 @@ public final class BatchReport {
               attributes_ = bs;
               break;
             }
-            case 66: {
-              if (!((mutable_bitField0_ & 0x00000080) == 0x00000080)) {
-                locations_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.IssueLocation>();
-                mutable_bitField0_ |= 0x00000080;
+            case 74: {
+              org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder subBuilder = null;
+              if (((bitField0_ & 0x00000080) == 0x00000080)) {
+                subBuilder = primaryLocation_.toBuilder();
+              }
+              primaryLocation_ = input.readMessage(org.sonar.batch.protocol.output.BatchReport.IssueLocation.PARSER, extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(primaryLocation_);
+                primaryLocation_ = subBuilder.buildPartial();
               }
-              locations_.add(input.readMessage(org.sonar.batch.protocol.output.BatchReport.IssueLocation.PARSER, extensionRegistry));
+              bitField0_ |= 0x00000080;
               break;
             }
-            case 74: {
+            case 82: {
               if (!((mutable_bitField0_ & 0x00000100) == 0x00000100)) {
-                executionFlows_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow>();
+                additionalLocation_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.IssueLocation>();
                 mutable_bitField0_ |= 0x00000100;
               }
-              executionFlows_.add(input.readMessage(org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.PARSER, extensionRegistry));
+              additionalLocation_.add(input.readMessage(org.sonar.batch.protocol.output.BatchReport.IssueLocation.PARSER, extensionRegistry));
+              break;
+            }
+            case 90: {
+              if (!((mutable_bitField0_ & 0x00000200) == 0x00000200)) {
+                executionFlow_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow>();
+                mutable_bitField0_ |= 0x00000200;
+              }
+              executionFlow_.add(input.readMessage(org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.PARSER, extensionRegistry));
               break;
             }
           }
@@ -6744,11 +6770,11 @@ public final class BatchReport {
         throw new com.google.protobuf.InvalidProtocolBufferException(
             e.getMessage()).setUnfinishedMessage(this);
       } finally {
-        if (((mutable_bitField0_ & 0x00000080) == 0x00000080)) {
-          locations_ = java.util.Collections.unmodifiableList(locations_);
-        }
         if (((mutable_bitField0_ & 0x00000100) == 0x00000100)) {
-          executionFlows_ = java.util.Collections.unmodifiableList(executionFlows_);
+          additionalLocation_ = java.util.Collections.unmodifiableList(additionalLocation_);
+        }
+        if (((mutable_bitField0_ & 0x00000200) == 0x00000200)) {
+          executionFlow_ = java.util.Collections.unmodifiableList(executionFlow_);
         }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
@@ -6995,74 +7021,95 @@ public final class BatchReport {
       }
     }
 
-    public static final int LOCATIONS_FIELD_NUMBER = 8;
-    private java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> locations_;
+    public static final int PRIMARY_LOCATION_FIELD_NUMBER = 9;
+    private org.sonar.batch.protocol.output.BatchReport.IssueLocation primaryLocation_;
+    /**
+     * <code>optional .IssueLocation primary_location = 9;</code>
+     */
+    public boolean hasPrimaryLocation() {
+      return ((bitField0_ & 0x00000080) == 0x00000080);
+    }
+    /**
+     * <code>optional .IssueLocation primary_location = 9;</code>
+     */
+    public org.sonar.batch.protocol.output.BatchReport.IssueLocation getPrimaryLocation() {
+      return primaryLocation_;
+    }
+    /**
+     * <code>optional .IssueLocation primary_location = 9;</code>
+     */
+    public org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getPrimaryLocationOrBuilder() {
+      return primaryLocation_;
+    }
+
+    public static final int ADDITIONAL_LOCATION_FIELD_NUMBER = 10;
+    private java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> additionalLocation_;
     /**
-     * <code>repeated .IssueLocation locations = 8;</code>
+     * <code>repeated .IssueLocation additional_location = 10;</code>
      */
-    public java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> getLocationsList() {
-      return locations_;
+    public java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> getAdditionalLocationList() {
+      return additionalLocation_;
     }
     /**
-     * <code>repeated .IssueLocation locations = 8;</code>
+     * <code>repeated .IssueLocation additional_location = 10;</code>
      */
     public java.util.List<? extends org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> 
-        getLocationsOrBuilderList() {
-      return locations_;
+        getAdditionalLocationOrBuilderList() {
+      return additionalLocation_;
     }
     /**
-     * <code>repeated .IssueLocation locations = 8;</code>
+     * <code>repeated .IssueLocation additional_location = 10;</code>
      */
-    public int getLocationsCount() {
-      return locations_.size();
+    public int getAdditionalLocationCount() {
+      return additionalLocation_.size();
     }
     /**
-     * <code>repeated .IssueLocation locations = 8;</code>
+     * <code>repeated .IssueLocation additional_location = 10;</code>
      */
-    public org.sonar.batch.protocol.output.BatchReport.IssueLocation getLocations(int index) {
-      return locations_.get(index);
+    public org.sonar.batch.protocol.output.BatchReport.IssueLocation getAdditionalLocation(int index) {
+      return additionalLocation_.get(index);
     }
     /**
-     * <code>repeated .IssueLocation locations = 8;</code>
+     * <code>repeated .IssueLocation additional_location = 10;</code>
      */
-    public org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getLocationsOrBuilder(
+    public org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getAdditionalLocationOrBuilder(
         int index) {
-      return locations_.get(index);
+      return additionalLocation_.get(index);
     }
 
-    public static final int EXECUTION_FLOWS_FIELD_NUMBER = 9;
-    private java.util.List<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow> executionFlows_;
+    public static final int EXECUTION_FLOW_FIELD_NUMBER = 11;
+    private java.util.List<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow> executionFlow_;
     /**
-     * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+     * <code>repeated .ExecutionFlow execution_flow = 11;</code>
      */
-    public java.util.List<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow> getExecutionFlowsList() {
-      return executionFlows_;
+    public java.util.List<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow> getExecutionFlowList() {
+      return executionFlow_;
     }
     /**
-     * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+     * <code>repeated .ExecutionFlow execution_flow = 11;</code>
      */
     public java.util.List<? extends org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder> 
-        getExecutionFlowsOrBuilderList() {
-      return executionFlows_;
+        getExecutionFlowOrBuilderList() {
+      return executionFlow_;
     }
     /**
-     * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+     * <code>repeated .ExecutionFlow execution_flow = 11;</code>
      */
-    public int getExecutionFlowsCount() {
-      return executionFlows_.size();
+    public int getExecutionFlowCount() {
+      return executionFlow_.size();
     }
     /**
-     * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+     * <code>repeated .ExecutionFlow execution_flow = 11;</code>
      */
-    public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow getExecutionFlows(int index) {
-      return executionFlows_.get(index);
+    public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow getExecutionFlow(int index) {
+      return executionFlow_.get(index);
     }
     /**
-     * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+     * <code>repeated .ExecutionFlow execution_flow = 11;</code>
      */
-    public org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder getExecutionFlowsOrBuilder(
+    public org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder getExecutionFlowOrBuilder(
         int index) {
-      return executionFlows_.get(index);
+      return executionFlow_.get(index);
     }
 
     private void initFields() {
@@ -7073,8 +7120,9 @@ public final class BatchReport {
       severity_ = org.sonar.batch.protocol.Constants.Severity.INFO;
       effortToFix_ = 0D;
       attributes_ = "";
-      locations_ = java.util.Collections.emptyList();
-      executionFlows_ = java.util.Collections.emptyList();
+      primaryLocation_ = org.sonar.batch.protocol.output.BatchReport.IssueLocation.getDefaultInstance();
+      additionalLocation_ = java.util.Collections.emptyList();
+      executionFlow_ = java.util.Collections.emptyList();
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -7110,11 +7158,14 @@ public final class BatchReport {
       if (((bitField0_ & 0x00000040) == 0x00000040)) {
         output.writeBytes(7, getAttributesBytes());
       }
-      for (int i = 0; i < locations_.size(); i++) {
-        output.writeMessage(8, locations_.get(i));
+      if (((bitField0_ & 0x00000080) == 0x00000080)) {
+        output.writeMessage(9, primaryLocation_);
+      }
+      for (int i = 0; i < additionalLocation_.size(); i++) {
+        output.writeMessage(10, additionalLocation_.get(i));
       }
-      for (int i = 0; i < executionFlows_.size(); i++) {
-        output.writeMessage(9, executionFlows_.get(i));
+      for (int i = 0; i < executionFlow_.size(); i++) {
+        output.writeMessage(11, executionFlow_.get(i));
       }
       getUnknownFields().writeTo(output);
     }
@@ -7153,13 +7204,17 @@ public final class BatchReport {
         size += com.google.protobuf.CodedOutputStream
           .computeBytesSize(7, getAttributesBytes());
       }
-      for (int i = 0; i < locations_.size(); i++) {
+      if (((bitField0_ & 0x00000080) == 0x00000080)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(9, primaryLocation_);
+      }
+      for (int i = 0; i < additionalLocation_.size(); i++) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(8, locations_.get(i));
+          .computeMessageSize(10, additionalLocation_.get(i));
       }
-      for (int i = 0; i < executionFlows_.size(); i++) {
+      for (int i = 0; i < executionFlow_.size(); i++) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(9, executionFlows_.get(i));
+          .computeMessageSize(11, executionFlow_.get(i));
       }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
@@ -7270,8 +7325,9 @@ public final class BatchReport {
       }
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
-          getLocationsFieldBuilder();
-          getExecutionFlowsFieldBuilder();
+          getPrimaryLocationFieldBuilder();
+          getAdditionalLocationFieldBuilder();
+          getExecutionFlowFieldBuilder();
         }
       }
       private static Builder create() {
@@ -7294,17 +7350,23 @@ public final class BatchReport {
         bitField0_ = (bitField0_ & ~0x00000020);
         attributes_ = "";
         bitField0_ = (bitField0_ & ~0x00000040);
-        if (locationsBuilder_ == null) {
-          locations_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000080);
+        if (primaryLocationBuilder_ == null) {
+          primaryLocation_ = org.sonar.batch.protocol.output.BatchReport.IssueLocation.getDefaultInstance();
         } else {
-          locationsBuilder_.clear();
+          primaryLocationBuilder_.clear();
         }
-        if (executionFlowsBuilder_ == null) {
-          executionFlows_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000080);
+        if (additionalLocationBuilder_ == null) {
+          additionalLocation_ = java.util.Collections.emptyList();
           bitField0_ = (bitField0_ & ~0x00000100);
         } else {
-          executionFlowsBuilder_.clear();
+          additionalLocationBuilder_.clear();
+        }
+        if (executionFlowBuilder_ == null) {
+          executionFlow_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000200);
+        } else {
+          executionFlowBuilder_.clear();
         }
         return this;
       }
@@ -7362,23 +7424,31 @@ public final class BatchReport {
           to_bitField0_ |= 0x00000040;
         }
         result.attributes_ = attributes_;
-        if (locationsBuilder_ == null) {
-          if (((bitField0_ & 0x00000080) == 0x00000080)) {
-            locations_ = java.util.Collections.unmodifiableList(locations_);
-            bitField0_ = (bitField0_ & ~0x00000080);
-          }
-          result.locations_ = locations_;
+        if (((from_bitField0_ & 0x00000080) == 0x00000080)) {
+          to_bitField0_ |= 0x00000080;
+        }
+        if (primaryLocationBuilder_ == null) {
+          result.primaryLocation_ = primaryLocation_;
         } else {
-          result.locations_ = locationsBuilder_.build();
+          result.primaryLocation_ = primaryLocationBuilder_.build();
         }
-        if (executionFlowsBuilder_ == null) {
+        if (additionalLocationBuilder_ == null) {
           if (((bitField0_ & 0x00000100) == 0x00000100)) {
-            executionFlows_ = java.util.Collections.unmodifiableList(executionFlows_);
+            additionalLocation_ = java.util.Collections.unmodifiableList(additionalLocation_);
             bitField0_ = (bitField0_ & ~0x00000100);
           }
-          result.executionFlows_ = executionFlows_;
+          result.additionalLocation_ = additionalLocation_;
         } else {
-          result.executionFlows_ = executionFlowsBuilder_.build();
+          result.additionalLocation_ = additionalLocationBuilder_.build();
+        }
+        if (executionFlowBuilder_ == null) {
+          if (((bitField0_ & 0x00000200) == 0x00000200)) {
+            executionFlow_ = java.util.Collections.unmodifiableList(executionFlow_);
+            bitField0_ = (bitField0_ & ~0x00000200);
+          }
+          result.executionFlow_ = executionFlow_;
+        } else {
+          result.executionFlow_ = executionFlowBuilder_.build();
         }
         result.bitField0_ = to_bitField0_;
         onBuilt();
@@ -7425,55 +7495,58 @@ public final class BatchReport {
           attributes_ = other.attributes_;
           onChanged();
         }
-        if (locationsBuilder_ == null) {
-          if (!other.locations_.isEmpty()) {
-            if (locations_.isEmpty()) {
-              locations_ = other.locations_;
-              bitField0_ = (bitField0_ & ~0x00000080);
+        if (other.hasPrimaryLocation()) {
+          mergePrimaryLocation(other.getPrimaryLocation());
+        }
+        if (additionalLocationBuilder_ == null) {
+          if (!other.additionalLocation_.isEmpty()) {
+            if (additionalLocation_.isEmpty()) {
+              additionalLocation_ = other.additionalLocation_;
+              bitField0_ = (bitField0_ & ~0x00000100);
             } else {
-              ensureLocationsIsMutable();
-              locations_.addAll(other.locations_);
+              ensureAdditionalLocationIsMutable();
+              additionalLocation_.addAll(other.additionalLocation_);
             }
             onChanged();
           }
         } else {
-          if (!other.locations_.isEmpty()) {
-            if (locationsBuilder_.isEmpty()) {
-              locationsBuilder_.dispose();
-              locationsBuilder_ = null;
-              locations_ = other.locations_;
-              bitField0_ = (bitField0_ & ~0x00000080);
-              locationsBuilder_ = 
+          if (!other.additionalLocation_.isEmpty()) {
+            if (additionalLocationBuilder_.isEmpty()) {
+              additionalLocationBuilder_.dispose();
+              additionalLocationBuilder_ = null;
+              additionalLocation_ = other.additionalLocation_;
+              bitField0_ = (bitField0_ & ~0x00000100);
+              additionalLocationBuilder_ = 
                 com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
-                   getLocationsFieldBuilder() : null;
+                   getAdditionalLocationFieldBuilder() : null;
             } else {
-              locationsBuilder_.addAllMessages(other.locations_);
+              additionalLocationBuilder_.addAllMessages(other.additionalLocation_);
             }
           }
         }
-        if (executionFlowsBuilder_ == null) {
-          if (!other.executionFlows_.isEmpty()) {
-            if (executionFlows_.isEmpty()) {
-              executionFlows_ = other.executionFlows_;
-              bitField0_ = (bitField0_ & ~0x00000100);
+        if (executionFlowBuilder_ == null) {
+          if (!other.executionFlow_.isEmpty()) {
+            if (executionFlow_.isEmpty()) {
+              executionFlow_ = other.executionFlow_;
+              bitField0_ = (bitField0_ & ~0x00000200);
             } else {
-              ensureExecutionFlowsIsMutable();
-              executionFlows_.addAll(other.executionFlows_);
+              ensureExecutionFlowIsMutable();
+              executionFlow_.addAll(other.executionFlow_);
             }
             onChanged();
           }
         } else {
-          if (!other.executionFlows_.isEmpty()) {
-            if (executionFlowsBuilder_.isEmpty()) {
-              executionFlowsBuilder_.dispose();
-              executionFlowsBuilder_ = null;
-              executionFlows_ = other.executionFlows_;
-              bitField0_ = (bitField0_ & ~0x00000100);
-              executionFlowsBuilder_ = 
+          if (!other.executionFlow_.isEmpty()) {
+            if (executionFlowBuilder_.isEmpty()) {
+              executionFlowBuilder_.dispose();
+              executionFlowBuilder_ = null;
+              executionFlow_ = other.executionFlow_;
+              bitField0_ = (bitField0_ & ~0x00000200);
+              executionFlowBuilder_ = 
                 com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
-                   getExecutionFlowsFieldBuilder() : null;
+                   getExecutionFlowFieldBuilder() : null;
             } else {
-              executionFlowsBuilder_.addAllMessages(other.executionFlows_);
+              executionFlowBuilder_.addAllMessages(other.executionFlow_);
             }
           }
         }
@@ -7907,484 +7980,600 @@ public final class BatchReport {
         return this;
       }
 
-      private java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> locations_ =
+      private org.sonar.batch.protocol.output.BatchReport.IssueLocation primaryLocation_ = org.sonar.batch.protocol.output.BatchReport.IssueLocation.getDefaultInstance();
+      private com.google.protobuf.SingleFieldBuilder<
+          org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> primaryLocationBuilder_;
+      /**
+       * <code>optional .IssueLocation primary_location = 9;</code>
+       */
+      public boolean hasPrimaryLocation() {
+        return ((bitField0_ & 0x00000080) == 0x00000080);
+      }
+      /**
+       * <code>optional .IssueLocation primary_location = 9;</code>
+       */
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocation getPrimaryLocation() {
+        if (primaryLocationBuilder_ == null) {
+          return primaryLocation_;
+        } else {
+          return primaryLocationBuilder_.getMessage();
+        }
+      }
+      /**
+       * <code>optional .IssueLocation primary_location = 9;</code>
+       */
+      public Builder setPrimaryLocation(org.sonar.batch.protocol.output.BatchReport.IssueLocation value) {
+        if (primaryLocationBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          primaryLocation_ = value;
+          onChanged();
+        } else {
+          primaryLocationBuilder_.setMessage(value);
+        }
+        bitField0_ |= 0x00000080;
+        return this;
+      }
+      /**
+       * <code>optional .IssueLocation primary_location = 9;</code>
+       */
+      public Builder setPrimaryLocation(
+          org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder builderForValue) {
+        if (primaryLocationBuilder_ == null) {
+          primaryLocation_ = builderForValue.build();
+          onChanged();
+        } else {
+          primaryLocationBuilder_.setMessage(builderForValue.build());
+        }
+        bitField0_ |= 0x00000080;
+        return this;
+      }
+      /**
+       * <code>optional .IssueLocation primary_location = 9;</code>
+       */
+      public Builder mergePrimaryLocation(org.sonar.batch.protocol.output.BatchReport.IssueLocation value) {
+        if (primaryLocationBuilder_ == null) {
+          if (((bitField0_ & 0x00000080) == 0x00000080) &&
+              primaryLocation_ != org.sonar.batch.protocol.output.BatchReport.IssueLocation.getDefaultInstance()) {
+            primaryLocation_ =
+              org.sonar.batch.protocol.output.BatchReport.IssueLocation.newBuilder(primaryLocation_).mergeFrom(value).buildPartial();
+          } else {
+            primaryLocation_ = value;
+          }
+          onChanged();
+        } else {
+          primaryLocationBuilder_.mergeFrom(value);
+        }
+        bitField0_ |= 0x00000080;
+        return this;
+      }
+      /**
+       * <code>optional .IssueLocation primary_location = 9;</code>
+       */
+      public Builder clearPrimaryLocation() {
+        if (primaryLocationBuilder_ == null) {
+          primaryLocation_ = org.sonar.batch.protocol.output.BatchReport.IssueLocation.getDefaultInstance();
+          onChanged();
+        } else {
+          primaryLocationBuilder_.clear();
+        }
+        bitField0_ = (bitField0_ & ~0x00000080);
+        return this;
+      }
+      /**
+       * <code>optional .IssueLocation primary_location = 9;</code>
+       */
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder getPrimaryLocationBuilder() {
+        bitField0_ |= 0x00000080;
+        onChanged();
+        return getPrimaryLocationFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>optional .IssueLocation primary_location = 9;</code>
+       */
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getPrimaryLocationOrBuilder() {
+        if (primaryLocationBuilder_ != null) {
+          return primaryLocationBuilder_.getMessageOrBuilder();
+        } else {
+          return primaryLocation_;
+        }
+      }
+      /**
+       * <code>optional .IssueLocation primary_location = 9;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilder<
+          org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> 
+          getPrimaryLocationFieldBuilder() {
+        if (primaryLocationBuilder_ == null) {
+          primaryLocationBuilder_ = new com.google.protobuf.SingleFieldBuilder<
+              org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder>(
+                  getPrimaryLocation(),
+                  getParentForChildren(),
+                  isClean());
+          primaryLocation_ = null;
+        }
+        return primaryLocationBuilder_;
+      }
+
+      private java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> additionalLocation_ =
         java.util.Collections.emptyList();
-      private void ensureLocationsIsMutable() {
-        if (!((bitField0_ & 0x00000080) == 0x00000080)) {
-          locations_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.IssueLocation>(locations_);
-          bitField0_ |= 0x00000080;
+      private void ensureAdditionalLocationIsMutable() {
+        if (!((bitField0_ & 0x00000100) == 0x00000100)) {
+          additionalLocation_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.IssueLocation>(additionalLocation_);
+          bitField0_ |= 0x00000100;
          }
       }
 
       private com.google.protobuf.RepeatedFieldBuilder<
-          org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> locationsBuilder_;
+          org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> additionalLocationBuilder_;
 
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> getLocationsList() {
-        if (locationsBuilder_ == null) {
-          return java.util.Collections.unmodifiableList(locations_);
+      public java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> getAdditionalLocationList() {
+        if (additionalLocationBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(additionalLocation_);
         } else {
-          return locationsBuilder_.getMessageList();
+          return additionalLocationBuilder_.getMessageList();
         }
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public int getLocationsCount() {
-        if (locationsBuilder_ == null) {
-          return locations_.size();
+      public int getAdditionalLocationCount() {
+        if (additionalLocationBuilder_ == null) {
+          return additionalLocation_.size();
         } else {
-          return locationsBuilder_.getCount();
+          return additionalLocationBuilder_.getCount();
         }
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.IssueLocation getLocations(int index) {
-        if (locationsBuilder_ == null) {
-          return locations_.get(index);
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocation getAdditionalLocation(int index) {
+        if (additionalLocationBuilder_ == null) {
+          return additionalLocation_.get(index);
         } else {
-          return locationsBuilder_.getMessage(index);
+          return additionalLocationBuilder_.getMessage(index);
         }
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public Builder setLocations(
+      public Builder setAdditionalLocation(
           int index, org.sonar.batch.protocol.output.BatchReport.IssueLocation value) {
-        if (locationsBuilder_ == null) {
+        if (additionalLocationBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          ensureLocationsIsMutable();
-          locations_.set(index, value);
+          ensureAdditionalLocationIsMutable();
+          additionalLocation_.set(index, value);
           onChanged();
         } else {
-          locationsBuilder_.setMessage(index, value);
+          additionalLocationBuilder_.setMessage(index, value);
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public Builder setLocations(
+      public Builder setAdditionalLocation(
           int index, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder builderForValue) {
-        if (locationsBuilder_ == null) {
-          ensureLocationsIsMutable();
-          locations_.set(index, builderForValue.build());
+        if (additionalLocationBuilder_ == null) {
+          ensureAdditionalLocationIsMutable();
+          additionalLocation_.set(index, builderForValue.build());
           onChanged();
         } else {
-          locationsBuilder_.setMessage(index, builderForValue.build());
+          additionalLocationBuilder_.setMessage(index, builderForValue.build());
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public Builder addLocations(org.sonar.batch.protocol.output.BatchReport.IssueLocation value) {
-        if (locationsBuilder_ == null) {
+      public Builder addAdditionalLocation(org.sonar.batch.protocol.output.BatchReport.IssueLocation value) {
+        if (additionalLocationBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          ensureLocationsIsMutable();
-          locations_.add(value);
+          ensureAdditionalLocationIsMutable();
+          additionalLocation_.add(value);
           onChanged();
         } else {
-          locationsBuilder_.addMessage(value);
+          additionalLocationBuilder_.addMessage(value);
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public Builder addLocations(
+      public Builder addAdditionalLocation(
           int index, org.sonar.batch.protocol.output.BatchReport.IssueLocation value) {
-        if (locationsBuilder_ == null) {
+        if (additionalLocationBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          ensureLocationsIsMutable();
-          locations_.add(index, value);
+          ensureAdditionalLocationIsMutable();
+          additionalLocation_.add(index, value);
           onChanged();
         } else {
-          locationsBuilder_.addMessage(index, value);
+          additionalLocationBuilder_.addMessage(index, value);
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public Builder addLocations(
+      public Builder addAdditionalLocation(
           org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder builderForValue) {
-        if (locationsBuilder_ == null) {
-          ensureLocationsIsMutable();
-          locations_.add(builderForValue.build());
+        if (additionalLocationBuilder_ == null) {
+          ensureAdditionalLocationIsMutable();
+          additionalLocation_.add(builderForValue.build());
           onChanged();
         } else {
-          locationsBuilder_.addMessage(builderForValue.build());
+          additionalLocationBuilder_.addMessage(builderForValue.build());
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public Builder addLocations(
+      public Builder addAdditionalLocation(
           int index, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder builderForValue) {
-        if (locationsBuilder_ == null) {
-          ensureLocationsIsMutable();
-          locations_.add(index, builderForValue.build());
+        if (additionalLocationBuilder_ == null) {
+          ensureAdditionalLocationIsMutable();
+          additionalLocation_.add(index, builderForValue.build());
           onChanged();
         } else {
-          locationsBuilder_.addMessage(index, builderForValue.build());
+          additionalLocationBuilder_.addMessage(index, builderForValue.build());
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public Builder addAllLocations(
+      public Builder addAllAdditionalLocation(
           java.lang.Iterable<? extends org.sonar.batch.protocol.output.BatchReport.IssueLocation> values) {
-        if (locationsBuilder_ == null) {
-          ensureLocationsIsMutable();
+        if (additionalLocationBuilder_ == null) {
+          ensureAdditionalLocationIsMutable();
           com.google.protobuf.AbstractMessageLite.Builder.addAll(
-              values, locations_);
+              values, additionalLocation_);
           onChanged();
         } else {
-          locationsBuilder_.addAllMessages(values);
+          additionalLocationBuilder_.addAllMessages(values);
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public Builder clearLocations() {
-        if (locationsBuilder_ == null) {
-          locations_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000080);
+      public Builder clearAdditionalLocation() {
+        if (additionalLocationBuilder_ == null) {
+          additionalLocation_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000100);
           onChanged();
         } else {
-          locationsBuilder_.clear();
+          additionalLocationBuilder_.clear();
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public Builder removeLocations(int index) {
-        if (locationsBuilder_ == null) {
-          ensureLocationsIsMutable();
-          locations_.remove(index);
+      public Builder removeAdditionalLocation(int index) {
+        if (additionalLocationBuilder_ == null) {
+          ensureAdditionalLocationIsMutable();
+          additionalLocation_.remove(index);
           onChanged();
         } else {
-          locationsBuilder_.remove(index);
+          additionalLocationBuilder_.remove(index);
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder getLocationsBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder getAdditionalLocationBuilder(
           int index) {
-        return getLocationsFieldBuilder().getBuilder(index);
+        return getAdditionalLocationFieldBuilder().getBuilder(index);
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getLocationsOrBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getAdditionalLocationOrBuilder(
           int index) {
-        if (locationsBuilder_ == null) {
-          return locations_.get(index);  } else {
-          return locationsBuilder_.getMessageOrBuilder(index);
+        if (additionalLocationBuilder_ == null) {
+          return additionalLocation_.get(index);  } else {
+          return additionalLocationBuilder_.getMessageOrBuilder(index);
         }
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
       public java.util.List<? extends org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> 
-           getLocationsOrBuilderList() {
-        if (locationsBuilder_ != null) {
-          return locationsBuilder_.getMessageOrBuilderList();
+           getAdditionalLocationOrBuilderList() {
+        if (additionalLocationBuilder_ != null) {
+          return additionalLocationBuilder_.getMessageOrBuilderList();
         } else {
-          return java.util.Collections.unmodifiableList(locations_);
+          return java.util.Collections.unmodifiableList(additionalLocation_);
         }
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder addLocationsBuilder() {
-        return getLocationsFieldBuilder().addBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder addAdditionalLocationBuilder() {
+        return getAdditionalLocationFieldBuilder().addBuilder(
             org.sonar.batch.protocol.output.BatchReport.IssueLocation.getDefaultInstance());
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder addLocationsBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder addAdditionalLocationBuilder(
           int index) {
-        return getLocationsFieldBuilder().addBuilder(
+        return getAdditionalLocationFieldBuilder().addBuilder(
             index, org.sonar.batch.protocol.output.BatchReport.IssueLocation.getDefaultInstance());
       }
       /**
-       * <code>repeated .IssueLocation locations = 8;</code>
+       * <code>repeated .IssueLocation additional_location = 10;</code>
        */
       public java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder> 
-           getLocationsBuilderList() {
-        return getLocationsFieldBuilder().getBuilderList();
+           getAdditionalLocationBuilderList() {
+        return getAdditionalLocationFieldBuilder().getBuilderList();
       }
       private com.google.protobuf.RepeatedFieldBuilder<
           org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> 
-          getLocationsFieldBuilder() {
-        if (locationsBuilder_ == null) {
-          locationsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
+          getAdditionalLocationFieldBuilder() {
+        if (additionalLocationBuilder_ == null) {
+          additionalLocationBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
               org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder>(
-                  locations_,
-                  ((bitField0_ & 0x00000080) == 0x00000080),
+                  additionalLocation_,
+                  ((bitField0_ & 0x00000100) == 0x00000100),
                   getParentForChildren(),
                   isClean());
-          locations_ = null;
+          additionalLocation_ = null;
         }
-        return locationsBuilder_;
+        return additionalLocationBuilder_;
       }
 
-      private java.util.List<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow> executionFlows_ =
+      private java.util.List<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow> executionFlow_ =
         java.util.Collections.emptyList();
-      private void ensureExecutionFlowsIsMutable() {
-        if (!((bitField0_ & 0x00000100) == 0x00000100)) {
-          executionFlows_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow>(executionFlows_);
-          bitField0_ |= 0x00000100;
+      private void ensureExecutionFlowIsMutable() {
+        if (!((bitField0_ & 0x00000200) == 0x00000200)) {
+          executionFlow_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow>(executionFlow_);
+          bitField0_ |= 0x00000200;
          }
       }
 
       private com.google.protobuf.RepeatedFieldBuilder<
-          org.sonar.batch.protocol.output.BatchReport.ExecutionFlow, org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder, org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder> executionFlowsBuilder_;
+          org.sonar.batch.protocol.output.BatchReport.ExecutionFlow, org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder, org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder> executionFlowBuilder_;
 
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public java.util.List<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow> getExecutionFlowsList() {
-        if (executionFlowsBuilder_ == null) {
-          return java.util.Collections.unmodifiableList(executionFlows_);
+      public java.util.List<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow> getExecutionFlowList() {
+        if (executionFlowBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(executionFlow_);
         } else {
-          return executionFlowsBuilder_.getMessageList();
+          return executionFlowBuilder_.getMessageList();
         }
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public int getExecutionFlowsCount() {
-        if (executionFlowsBuilder_ == null) {
-          return executionFlows_.size();
+      public int getExecutionFlowCount() {
+        if (executionFlowBuilder_ == null) {
+          return executionFlow_.size();
         } else {
-          return executionFlowsBuilder_.getCount();
+          return executionFlowBuilder_.getCount();
         }
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow getExecutionFlows(int index) {
-        if (executionFlowsBuilder_ == null) {
-          return executionFlows_.get(index);
+      public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow getExecutionFlow(int index) {
+        if (executionFlowBuilder_ == null) {
+          return executionFlow_.get(index);
         } else {
-          return executionFlowsBuilder_.getMessage(index);
+          return executionFlowBuilder_.getMessage(index);
         }
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public Builder setExecutionFlows(
+      public Builder setExecutionFlow(
           int index, org.sonar.batch.protocol.output.BatchReport.ExecutionFlow value) {
-        if (executionFlowsBuilder_ == null) {
+        if (executionFlowBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          ensureExecutionFlowsIsMutable();
-          executionFlows_.set(index, value);
+          ensureExecutionFlowIsMutable();
+          executionFlow_.set(index, value);
           onChanged();
         } else {
-          executionFlowsBuilder_.setMessage(index, value);
+          executionFlowBuilder_.setMessage(index, value);
         }
         return this;
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public Builder setExecutionFlows(
+      public Builder setExecutionFlow(
           int index, org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder builderForValue) {
-        if (executionFlowsBuilder_ == null) {
-          ensureExecutionFlowsIsMutable();
-          executionFlows_.set(index, builderForValue.build());
+        if (executionFlowBuilder_ == null) {
+          ensureExecutionFlowIsMutable();
+          executionFlow_.set(index, builderForValue.build());
           onChanged();
         } else {
-          executionFlowsBuilder_.setMessage(index, builderForValue.build());
+          executionFlowBuilder_.setMessage(index, builderForValue.build());
         }
         return this;
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public Builder addExecutionFlows(org.sonar.batch.protocol.output.BatchReport.ExecutionFlow value) {
-        if (executionFlowsBuilder_ == null) {
+      public Builder addExecutionFlow(org.sonar.batch.protocol.output.BatchReport.ExecutionFlow value) {
+        if (executionFlowBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          ensureExecutionFlowsIsMutable();
-          executionFlows_.add(value);
+          ensureExecutionFlowIsMutable();
+          executionFlow_.add(value);
           onChanged();
         } else {
-          executionFlowsBuilder_.addMessage(value);
+          executionFlowBuilder_.addMessage(value);
         }
         return this;
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public Builder addExecutionFlows(
+      public Builder addExecutionFlow(
           int index, org.sonar.batch.protocol.output.BatchReport.ExecutionFlow value) {
-        if (executionFlowsBuilder_ == null) {
+        if (executionFlowBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          ensureExecutionFlowsIsMutable();
-          executionFlows_.add(index, value);
+          ensureExecutionFlowIsMutable();
+          executionFlow_.add(index, value);
           onChanged();
         } else {
-          executionFlowsBuilder_.addMessage(index, value);
+          executionFlowBuilder_.addMessage(index, value);
         }
         return this;
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public Builder addExecutionFlows(
+      public Builder addExecutionFlow(
           org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder builderForValue) {
-        if (executionFlowsBuilder_ == null) {
-          ensureExecutionFlowsIsMutable();
-          executionFlows_.add(builderForValue.build());
+        if (executionFlowBuilder_ == null) {
+          ensureExecutionFlowIsMutable();
+          executionFlow_.add(builderForValue.build());
           onChanged();
         } else {
-          executionFlowsBuilder_.addMessage(builderForValue.build());
+          executionFlowBuilder_.addMessage(builderForValue.build());
         }
         return this;
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public Builder addExecutionFlows(
+      public Builder addExecutionFlow(
           int index, org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder builderForValue) {
-        if (executionFlowsBuilder_ == null) {
-          ensureExecutionFlowsIsMutable();
-          executionFlows_.add(index, builderForValue.build());
+        if (executionFlowBuilder_ == null) {
+          ensureExecutionFlowIsMutable();
+          executionFlow_.add(index, builderForValue.build());
           onChanged();
         } else {
-          executionFlowsBuilder_.addMessage(index, builderForValue.build());
+          executionFlowBuilder_.addMessage(index, builderForValue.build());
         }
         return this;
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public Builder addAllExecutionFlows(
+      public Builder addAllExecutionFlow(
           java.lang.Iterable<? extends org.sonar.batch.protocol.output.BatchReport.ExecutionFlow> values) {
-        if (executionFlowsBuilder_ == null) {
-          ensureExecutionFlowsIsMutable();
+        if (executionFlowBuilder_ == null) {
+          ensureExecutionFlowIsMutable();
           com.google.protobuf.AbstractMessageLite.Builder.addAll(
-              values, executionFlows_);
+              values, executionFlow_);
           onChanged();
         } else {
-          executionFlowsBuilder_.addAllMessages(values);
+          executionFlowBuilder_.addAllMessages(values);
         }
         return this;
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public Builder clearExecutionFlows() {
-        if (executionFlowsBuilder_ == null) {
-          executionFlows_ = java.util.Collections.emptyList();
-          bitField0_ = (bitField0_ & ~0x00000100);
+      public Builder clearExecutionFlow() {
+        if (executionFlowBuilder_ == null) {
+          executionFlow_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000200);
           onChanged();
         } else {
-          executionFlowsBuilder_.clear();
+          executionFlowBuilder_.clear();
         }
         return this;
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public Builder removeExecutionFlows(int index) {
-        if (executionFlowsBuilder_ == null) {
-          ensureExecutionFlowsIsMutable();
-          executionFlows_.remove(index);
+      public Builder removeExecutionFlow(int index) {
+        if (executionFlowBuilder_ == null) {
+          ensureExecutionFlowIsMutable();
+          executionFlow_.remove(index);
           onChanged();
         } else {
-          executionFlowsBuilder_.remove(index);
+          executionFlowBuilder_.remove(index);
         }
         return this;
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder getExecutionFlowsBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder getExecutionFlowBuilder(
           int index) {
-        return getExecutionFlowsFieldBuilder().getBuilder(index);
+        return getExecutionFlowFieldBuilder().getBuilder(index);
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder getExecutionFlowsOrBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder getExecutionFlowOrBuilder(
           int index) {
-        if (executionFlowsBuilder_ == null) {
-          return executionFlows_.get(index);  } else {
-          return executionFlowsBuilder_.getMessageOrBuilder(index);
+        if (executionFlowBuilder_ == null) {
+          return executionFlow_.get(index);  } else {
+          return executionFlowBuilder_.getMessageOrBuilder(index);
         }
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
       public java.util.List<? extends org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder> 
-           getExecutionFlowsOrBuilderList() {
-        if (executionFlowsBuilder_ != null) {
-          return executionFlowsBuilder_.getMessageOrBuilderList();
+           getExecutionFlowOrBuilderList() {
+        if (executionFlowBuilder_ != null) {
+          return executionFlowBuilder_.getMessageOrBuilderList();
         } else {
-          return java.util.Collections.unmodifiableList(executionFlows_);
+          return java.util.Collections.unmodifiableList(executionFlow_);
         }
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder addExecutionFlowsBuilder() {
-        return getExecutionFlowsFieldBuilder().addBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder addExecutionFlowBuilder() {
+        return getExecutionFlowFieldBuilder().addBuilder(
             org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.getDefaultInstance());
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder addExecutionFlowsBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder addExecutionFlowBuilder(
           int index) {
-        return getExecutionFlowsFieldBuilder().addBuilder(
+        return getExecutionFlowFieldBuilder().addBuilder(
             index, org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.getDefaultInstance());
       }
       /**
-       * <code>repeated .ExecutionFlow execution_flows = 9;</code>
+       * <code>repeated .ExecutionFlow execution_flow = 11;</code>
        */
       public java.util.List<org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder> 
-           getExecutionFlowsBuilderList() {
-        return getExecutionFlowsFieldBuilder().getBuilderList();
+           getExecutionFlowBuilderList() {
+        return getExecutionFlowFieldBuilder().getBuilderList();
       }
       private com.google.protobuf.RepeatedFieldBuilder<
           org.sonar.batch.protocol.output.BatchReport.ExecutionFlow, org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder, org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder> 
-          getExecutionFlowsFieldBuilder() {
-        if (executionFlowsBuilder_ == null) {
-          executionFlowsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
+          getExecutionFlowFieldBuilder() {
+        if (executionFlowBuilder_ == null) {
+          executionFlowBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
               org.sonar.batch.protocol.output.BatchReport.ExecutionFlow, org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.Builder, org.sonar.batch.protocol.output.BatchReport.ExecutionFlowOrBuilder>(
-                  executionFlows_,
-                  ((bitField0_ & 0x00000100) == 0x00000100),
+                  executionFlow_,
+                  ((bitField0_ & 0x00000200) == 0x00000200),
                   getParentForChildren(),
                   isClean());
-          executionFlows_ = null;
+          executionFlow_ = null;
         }
-        return executionFlowsBuilder_;
+        return executionFlowBuilder_;
       }
 
       // @@protoc_insertion_point(builder_scope:Issue)
@@ -9206,27 +9395,27 @@ public final class BatchReport {
       com.google.protobuf.MessageOrBuilder {
 
     /**
-     * <code>repeated .IssueLocation locations = 1;</code>
+     * <code>repeated .IssueLocation location = 1;</code>
      */
     java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> 
-        getLocationsList();
+        getLocationList();
     /**
-     * <code>repeated .IssueLocation locations = 1;</code>
+     * <code>repeated .IssueLocation location = 1;</code>
      */
-    org.sonar.batch.protocol.output.BatchReport.IssueLocation getLocations(int index);
+    org.sonar.batch.protocol.output.BatchReport.IssueLocation getLocation(int index);
     /**
-     * <code>repeated .IssueLocation locations = 1;</code>
+     * <code>repeated .IssueLocation location = 1;</code>
      */
-    int getLocationsCount();
+    int getLocationCount();
     /**
-     * <code>repeated .IssueLocation locations = 1;</code>
+     * <code>repeated .IssueLocation location = 1;</code>
      */
     java.util.List<? extends org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> 
-        getLocationsOrBuilderList();
+        getLocationOrBuilderList();
     /**
-     * <code>repeated .IssueLocation locations = 1;</code>
+     * <code>repeated .IssueLocation location = 1;</code>
      */
-    org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getLocationsOrBuilder(
+    org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getLocationOrBuilder(
         int index);
   }
   /**
@@ -9283,10 +9472,10 @@ public final class BatchReport {
             }
             case 10: {
               if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) {
-                locations_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.IssueLocation>();
+                location_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.IssueLocation>();
                 mutable_bitField0_ |= 0x00000001;
               }
-              locations_.add(input.readMessage(org.sonar.batch.protocol.output.BatchReport.IssueLocation.PARSER, extensionRegistry));
+              location_.add(input.readMessage(org.sonar.batch.protocol.output.BatchReport.IssueLocation.PARSER, extensionRegistry));
               break;
             }
           }
@@ -9298,7 +9487,7 @@ public final class BatchReport {
             e.getMessage()).setUnfinishedMessage(this);
       } finally {
         if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) {
-          locations_ = java.util.Collections.unmodifiableList(locations_);
+          location_ = java.util.Collections.unmodifiableList(location_);
         }
         this.unknownFields = unknownFields.build();
         makeExtensionsImmutable();
@@ -9331,43 +9520,43 @@ public final class BatchReport {
       return PARSER;
     }
 
-    public static final int LOCATIONS_FIELD_NUMBER = 1;
-    private java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> locations_;
+    public static final int LOCATION_FIELD_NUMBER = 1;
+    private java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> location_;
     /**
-     * <code>repeated .IssueLocation locations = 1;</code>
+     * <code>repeated .IssueLocation location = 1;</code>
      */
-    public java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> getLocationsList() {
-      return locations_;
+    public java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> getLocationList() {
+      return location_;
     }
     /**
-     * <code>repeated .IssueLocation locations = 1;</code>
+     * <code>repeated .IssueLocation location = 1;</code>
      */
     public java.util.List<? extends org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> 
-        getLocationsOrBuilderList() {
-      return locations_;
+        getLocationOrBuilderList() {
+      return location_;
     }
     /**
-     * <code>repeated .IssueLocation locations = 1;</code>
+     * <code>repeated .IssueLocation location = 1;</code>
      */
-    public int getLocationsCount() {
-      return locations_.size();
+    public int getLocationCount() {
+      return location_.size();
     }
     /**
-     * <code>repeated .IssueLocation locations = 1;</code>
+     * <code>repeated .IssueLocation location = 1;</code>
      */
-    public org.sonar.batch.protocol.output.BatchReport.IssueLocation getLocations(int index) {
-      return locations_.get(index);
+    public org.sonar.batch.protocol.output.BatchReport.IssueLocation getLocation(int index) {
+      return location_.get(index);
     }
     /**
-     * <code>repeated .IssueLocation locations = 1;</code>
+     * <code>repeated .IssueLocation location = 1;</code>
      */
-    public org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getLocationsOrBuilder(
+    public org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getLocationOrBuilder(
         int index) {
-      return locations_.get(index);
+      return location_.get(index);
     }
 
     private void initFields() {
-      locations_ = java.util.Collections.emptyList();
+      location_ = java.util.Collections.emptyList();
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -9382,8 +9571,8 @@ public final class BatchReport {
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
       getSerializedSize();
-      for (int i = 0; i < locations_.size(); i++) {
-        output.writeMessage(1, locations_.get(i));
+      for (int i = 0; i < location_.size(); i++) {
+        output.writeMessage(1, location_.get(i));
       }
       getUnknownFields().writeTo(output);
     }
@@ -9394,9 +9583,9 @@ public final class BatchReport {
       if (size != -1) return size;
 
       size = 0;
-      for (int i = 0; i < locations_.size(); i++) {
+      for (int i = 0; i < location_.size(); i++) {
         size += com.google.protobuf.CodedOutputStream
-          .computeMessageSize(1, locations_.get(i));
+          .computeMessageSize(1, location_.get(i));
       }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
@@ -9507,7 +9696,7 @@ public final class BatchReport {
       }
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
-          getLocationsFieldBuilder();
+          getLocationFieldBuilder();
         }
       }
       private static Builder create() {
@@ -9516,11 +9705,11 @@ public final class BatchReport {
 
       public Builder clear() {
         super.clear();
-        if (locationsBuilder_ == null) {
-          locations_ = java.util.Collections.emptyList();
+        if (locationBuilder_ == null) {
+          location_ = java.util.Collections.emptyList();
           bitField0_ = (bitField0_ & ~0x00000001);
         } else {
-          locationsBuilder_.clear();
+          locationBuilder_.clear();
         }
         return this;
       }
@@ -9549,14 +9738,14 @@ public final class BatchReport {
       public org.sonar.batch.protocol.output.BatchReport.ExecutionFlow buildPartial() {
         org.sonar.batch.protocol.output.BatchReport.ExecutionFlow result = new org.sonar.batch.protocol.output.BatchReport.ExecutionFlow(this);
         int from_bitField0_ = bitField0_;
-        if (locationsBuilder_ == null) {
+        if (locationBuilder_ == null) {
           if (((bitField0_ & 0x00000001) == 0x00000001)) {
-            locations_ = java.util.Collections.unmodifiableList(locations_);
+            location_ = java.util.Collections.unmodifiableList(location_);
             bitField0_ = (bitField0_ & ~0x00000001);
           }
-          result.locations_ = locations_;
+          result.location_ = location_;
         } else {
-          result.locations_ = locationsBuilder_.build();
+          result.location_ = locationBuilder_.build();
         }
         onBuilt();
         return result;
@@ -9573,29 +9762,29 @@ public final class BatchReport {
 
       public Builder mergeFrom(org.sonar.batch.protocol.output.BatchReport.ExecutionFlow other) {
         if (other == org.sonar.batch.protocol.output.BatchReport.ExecutionFlow.getDefaultInstance()) return this;
-        if (locationsBuilder_ == null) {
-          if (!other.locations_.isEmpty()) {
-            if (locations_.isEmpty()) {
-              locations_ = other.locations_;
+        if (locationBuilder_ == null) {
+          if (!other.location_.isEmpty()) {
+            if (location_.isEmpty()) {
+              location_ = other.location_;
               bitField0_ = (bitField0_ & ~0x00000001);
             } else {
-              ensureLocationsIsMutable();
-              locations_.addAll(other.locations_);
+              ensureLocationIsMutable();
+              location_.addAll(other.location_);
             }
             onChanged();
           }
         } else {
-          if (!other.locations_.isEmpty()) {
-            if (locationsBuilder_.isEmpty()) {
-              locationsBuilder_.dispose();
-              locationsBuilder_ = null;
-              locations_ = other.locations_;
+          if (!other.location_.isEmpty()) {
+            if (locationBuilder_.isEmpty()) {
+              locationBuilder_.dispose();
+              locationBuilder_ = null;
+              location_ = other.location_;
               bitField0_ = (bitField0_ & ~0x00000001);
-              locationsBuilder_ = 
+              locationBuilder_ = 
                 com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
-                   getLocationsFieldBuilder() : null;
+                   getLocationFieldBuilder() : null;
             } else {
-              locationsBuilder_.addAllMessages(other.locations_);
+              locationBuilder_.addAllMessages(other.location_);
             }
           }
         }
@@ -9626,244 +9815,244 @@ public final class BatchReport {
       }
       private int bitField0_;
 
-      private java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> locations_ =
+      private java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> location_ =
         java.util.Collections.emptyList();
-      private void ensureLocationsIsMutable() {
+      private void ensureLocationIsMutable() {
         if (!((bitField0_ & 0x00000001) == 0x00000001)) {
-          locations_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.IssueLocation>(locations_);
+          location_ = new java.util.ArrayList<org.sonar.batch.protocol.output.BatchReport.IssueLocation>(location_);
           bitField0_ |= 0x00000001;
          }
       }
 
       private com.google.protobuf.RepeatedFieldBuilder<
-          org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> locationsBuilder_;
+          org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> locationBuilder_;
 
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> getLocationsList() {
-        if (locationsBuilder_ == null) {
-          return java.util.Collections.unmodifiableList(locations_);
+      public java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation> getLocationList() {
+        if (locationBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(location_);
         } else {
-          return locationsBuilder_.getMessageList();
+          return locationBuilder_.getMessageList();
         }
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public int getLocationsCount() {
-        if (locationsBuilder_ == null) {
-          return locations_.size();
+      public int getLocationCount() {
+        if (locationBuilder_ == null) {
+          return location_.size();
         } else {
-          return locationsBuilder_.getCount();
+          return locationBuilder_.getCount();
         }
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.IssueLocation getLocations(int index) {
-        if (locationsBuilder_ == null) {
-          return locations_.get(index);
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocation getLocation(int index) {
+        if (locationBuilder_ == null) {
+          return location_.get(index);
         } else {
-          return locationsBuilder_.getMessage(index);
+          return locationBuilder_.getMessage(index);
         }
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public Builder setLocations(
+      public Builder setLocation(
           int index, org.sonar.batch.protocol.output.BatchReport.IssueLocation value) {
-        if (locationsBuilder_ == null) {
+        if (locationBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          ensureLocationsIsMutable();
-          locations_.set(index, value);
+          ensureLocationIsMutable();
+          location_.set(index, value);
           onChanged();
         } else {
-          locationsBuilder_.setMessage(index, value);
+          locationBuilder_.setMessage(index, value);
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public Builder setLocations(
+      public Builder setLocation(
           int index, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder builderForValue) {
-        if (locationsBuilder_ == null) {
-          ensureLocationsIsMutable();
-          locations_.set(index, builderForValue.build());
+        if (locationBuilder_ == null) {
+          ensureLocationIsMutable();
+          location_.set(index, builderForValue.build());
           onChanged();
         } else {
-          locationsBuilder_.setMessage(index, builderForValue.build());
+          locationBuilder_.setMessage(index, builderForValue.build());
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public Builder addLocations(org.sonar.batch.protocol.output.BatchReport.IssueLocation value) {
-        if (locationsBuilder_ == null) {
+      public Builder addLocation(org.sonar.batch.protocol.output.BatchReport.IssueLocation value) {
+        if (locationBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          ensureLocationsIsMutable();
-          locations_.add(value);
+          ensureLocationIsMutable();
+          location_.add(value);
           onChanged();
         } else {
-          locationsBuilder_.addMessage(value);
+          locationBuilder_.addMessage(value);
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public Builder addLocations(
+      public Builder addLocation(
           int index, org.sonar.batch.protocol.output.BatchReport.IssueLocation value) {
-        if (locationsBuilder_ == null) {
+        if (locationBuilder_ == null) {
           if (value == null) {
             throw new NullPointerException();
           }
-          ensureLocationsIsMutable();
-          locations_.add(index, value);
+          ensureLocationIsMutable();
+          location_.add(index, value);
           onChanged();
         } else {
-          locationsBuilder_.addMessage(index, value);
+          locationBuilder_.addMessage(index, value);
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public Builder addLocations(
+      public Builder addLocation(
           org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder builderForValue) {
-        if (locationsBuilder_ == null) {
-          ensureLocationsIsMutable();
-          locations_.add(builderForValue.build());
+        if (locationBuilder_ == null) {
+          ensureLocationIsMutable();
+          location_.add(builderForValue.build());
           onChanged();
         } else {
-          locationsBuilder_.addMessage(builderForValue.build());
+          locationBuilder_.addMessage(builderForValue.build());
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public Builder addLocations(
+      public Builder addLocation(
           int index, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder builderForValue) {
-        if (locationsBuilder_ == null) {
-          ensureLocationsIsMutable();
-          locations_.add(index, builderForValue.build());
+        if (locationBuilder_ == null) {
+          ensureLocationIsMutable();
+          location_.add(index, builderForValue.build());
           onChanged();
         } else {
-          locationsBuilder_.addMessage(index, builderForValue.build());
+          locationBuilder_.addMessage(index, builderForValue.build());
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public Builder addAllLocations(
+      public Builder addAllLocation(
           java.lang.Iterable<? extends org.sonar.batch.protocol.output.BatchReport.IssueLocation> values) {
-        if (locationsBuilder_ == null) {
-          ensureLocationsIsMutable();
+        if (locationBuilder_ == null) {
+          ensureLocationIsMutable();
           com.google.protobuf.AbstractMessageLite.Builder.addAll(
-              values, locations_);
+              values, location_);
           onChanged();
         } else {
-          locationsBuilder_.addAllMessages(values);
+          locationBuilder_.addAllMessages(values);
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public Builder clearLocations() {
-        if (locationsBuilder_ == null) {
-          locations_ = java.util.Collections.emptyList();
+      public Builder clearLocation() {
+        if (locationBuilder_ == null) {
+          location_ = java.util.Collections.emptyList();
           bitField0_ = (bitField0_ & ~0x00000001);
           onChanged();
         } else {
-          locationsBuilder_.clear();
+          locationBuilder_.clear();
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public Builder removeLocations(int index) {
-        if (locationsBuilder_ == null) {
-          ensureLocationsIsMutable();
-          locations_.remove(index);
+      public Builder removeLocation(int index) {
+        if (locationBuilder_ == null) {
+          ensureLocationIsMutable();
+          location_.remove(index);
           onChanged();
         } else {
-          locationsBuilder_.remove(index);
+          locationBuilder_.remove(index);
         }
         return this;
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder getLocationsBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder getLocationBuilder(
           int index) {
-        return getLocationsFieldBuilder().getBuilder(index);
+        return getLocationFieldBuilder().getBuilder(index);
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getLocationsOrBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder getLocationOrBuilder(
           int index) {
-        if (locationsBuilder_ == null) {
-          return locations_.get(index);  } else {
-          return locationsBuilder_.getMessageOrBuilder(index);
+        if (locationBuilder_ == null) {
+          return location_.get(index);  } else {
+          return locationBuilder_.getMessageOrBuilder(index);
         }
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
       public java.util.List<? extends org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> 
-           getLocationsOrBuilderList() {
-        if (locationsBuilder_ != null) {
-          return locationsBuilder_.getMessageOrBuilderList();
+           getLocationOrBuilderList() {
+        if (locationBuilder_ != null) {
+          return locationBuilder_.getMessageOrBuilderList();
         } else {
-          return java.util.Collections.unmodifiableList(locations_);
+          return java.util.Collections.unmodifiableList(location_);
         }
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder addLocationsBuilder() {
-        return getLocationsFieldBuilder().addBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder addLocationBuilder() {
+        return getLocationFieldBuilder().addBuilder(
             org.sonar.batch.protocol.output.BatchReport.IssueLocation.getDefaultInstance());
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
-      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder addLocationsBuilder(
+      public org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder addLocationBuilder(
           int index) {
-        return getLocationsFieldBuilder().addBuilder(
+        return getLocationFieldBuilder().addBuilder(
             index, org.sonar.batch.protocol.output.BatchReport.IssueLocation.getDefaultInstance());
       }
       /**
-       * <code>repeated .IssueLocation locations = 1;</code>
+       * <code>repeated .IssueLocation location = 1;</code>
        */
       public java.util.List<org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder> 
-           getLocationsBuilderList() {
-        return getLocationsFieldBuilder().getBuilderList();
+           getLocationBuilderList() {
+        return getLocationFieldBuilder().getBuilderList();
       }
       private com.google.protobuf.RepeatedFieldBuilder<
           org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder> 
-          getLocationsFieldBuilder() {
-        if (locationsBuilder_ == null) {
-          locationsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
+          getLocationFieldBuilder() {
+        if (locationBuilder_ == null) {
+          locationBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
               org.sonar.batch.protocol.output.BatchReport.IssueLocation, org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder, org.sonar.batch.protocol.output.BatchReport.IssueLocationOrBuilder>(
-                  locations_,
+                  location_,
                   ((bitField0_ & 0x00000001) == 0x00000001),
                   getParentForChildren(),
                   isClean());
-          locations_ = null;
+          location_ = null;
         }
-        return locationsBuilder_;
+        return locationBuilder_;
       }
 
       // @@protoc_insertion_point(builder_scope:ExecutionFlow)
@@ -19101,43 +19290,44 @@ public final class BatchReport {
       "_type\030\001 \001(\0162\021.MeasureValueType\022\025\n\rboolea" +
       "n_value\030\002 \001(\010\022\021\n\tint_value\030\003 \001(\005\022\022\n\nlong" +
       "_value\030\004 \001(\003\022\024\n\014double_value\030\005 \001(\001\022\024\n\014st" +
-      "ring_value\030\006 \001(\t\022\022\n\nmetric_key\030\007 \001(\t\"\341\001\n",
+      "ring_value\030\006 \001(\t\022\022\n\nmetric_key\030\007 \001(\t\"\224\002\n",
       "\005Issue\022\027\n\017rule_repository\030\001 \001(\t\022\020\n\010rule_" +
       "key\030\002 \001(\t\022\014\n\004line\030\003 \001(\005\022\013\n\003msg\030\004 \001(\t\022\033\n\010" +
       "severity\030\005 \001(\0162\t.Severity\022\025\n\reffort_to_f" +
-      "ix\030\006 \001(\001\022\022\n\nattributes\030\007 \001(\t\022!\n\tlocation" +
-      "s\030\010 \003(\0132\016.IssueLocation\022\'\n\017execution_flo" +
-      "ws\030\t \003(\0132\016.ExecutionFlow\"S\n\rIssueLocatio" +
-      "n\022\025\n\rcomponent_ref\030\001 \001(\005\022\036\n\ntext_range\030\002" +
-      " \001(\0132\n.TextRange\022\013\n\003msg\030\003 \001(\t\"2\n\rExecuti" +
-      "onFlow\022!\n\tlocations\030\001 \003(\0132\016.IssueLocatio" +
-      "n\"\254\001\n\nChangesets\022\025\n\rcomponent_ref\030\001 \001(\005\022",
-      "(\n\tchangeset\030\002 \003(\0132\025.Changesets.Changese" +
-      "t\022 \n\024changesetIndexByLine\030\003 \003(\005B\002\020\001\032;\n\tC" +
-      "hangeset\022\020\n\010revision\030\001 \001(\t\022\016\n\006author\030\002 \001" +
-      "(\t\022\014\n\004date\030\003 \001(\003\"V\n\tDuplicate\022\026\n\016other_f" +
-      "ile_ref\030\001 \001(\005\022\031\n\005range\030\002 \001(\0132\n.TextRange" +
-      "\022\026\n\016other_file_key\030\003 \001(\t\"Q\n\013Duplication\022" +
-      "#\n\017origin_position\030\001 \001(\0132\n.TextRange\022\035\n\t" +
-      "duplicate\030\002 \003(\0132\n.Duplicate\"[\n\tTextRange" +
-      "\022\022\n\nstart_line\030\001 \001(\005\022\020\n\010end_line\030\002 \001(\005\022\024" +
-      "\n\014start_offset\030\003 \001(\005\022\022\n\nend_offset\030\004 \001(\005",
-      "\"H\n\006Symbol\022\037\n\013declaration\030\001 \001(\0132\n.TextRa" +
-      "nge\022\035\n\treference\030\002 \003(\0132\n.TextRange\"\260\001\n\010C" +
-      "overage\022\014\n\004line\030\001 \001(\005\022\022\n\nconditions\030\002 \001(" +
-      "\005\022\017\n\007ut_hits\030\003 \001(\010\022\017\n\007it_hits\030\004 \001(\010\022\035\n\025u" +
-      "t_covered_conditions\030\005 \001(\005\022\035\n\025it_covered" +
-      "_conditions\030\006 \001(\005\022\"\n\032overall_covered_con" +
-      "ditions\030\007 \001(\005\"P\n\022SyntaxHighlighting\022\031\n\005r" +
-      "ange\030\001 \001(\0132\n.TextRange\022\037\n\004type\030\002 \001(\0162\021.H" +
-      "ighlightingType\"j\n\004Test\022\014\n\004name\030\001 \001(\t\022\033\n" +
-      "\006status\030\002 \001(\0162\013.TestStatus\022\026\n\016duration_i",
-      "n_ms\030\003 \001(\003\022\022\n\nstacktrace\030\004 \001(\t\022\013\n\003msg\030\005 " +
-      "\001(\t\"\221\001\n\016CoverageDetail\022\021\n\ttest_name\030\001 \001(" +
-      "\t\0221\n\014covered_file\030\002 \003(\0132\033.CoverageDetail" +
-      ".CoveredFile\0329\n\013CoveredFile\022\020\n\010file_ref\030" +
-      "\001 \001(\005\022\030\n\014covered_line\030\002 \003(\005B\002\020\001B#\n\037org.s" +
-      "onar.batch.protocol.outputH\001"
+      "ix\030\006 \001(\001\022\022\n\nattributes\030\007 \001(\t\022(\n\020primary_" +
+      "location\030\t \001(\0132\016.IssueLocation\022+\n\023additi" +
+      "onal_location\030\n \003(\0132\016.IssueLocation\022&\n\016e" +
+      "xecution_flow\030\013 \003(\0132\016.ExecutionFlow\"S\n\rI" +
+      "ssueLocation\022\025\n\rcomponent_ref\030\001 \001(\005\022\036\n\nt" +
+      "ext_range\030\002 \001(\0132\n.TextRange\022\013\n\003msg\030\003 \001(\t" +
+      "\"1\n\rExecutionFlow\022 \n\010location\030\001 \003(\0132\016.Is",
+      "sueLocation\"\254\001\n\nChangesets\022\025\n\rcomponent_" +
+      "ref\030\001 \001(\005\022(\n\tchangeset\030\002 \003(\0132\025.Changeset" +
+      "s.Changeset\022 \n\024changesetIndexByLine\030\003 \003(" +
+      "\005B\002\020\001\032;\n\tChangeset\022\020\n\010revision\030\001 \001(\t\022\016\n\006" +
+      "author\030\002 \001(\t\022\014\n\004date\030\003 \001(\003\"V\n\tDuplicate\022" +
+      "\026\n\016other_file_ref\030\001 \001(\005\022\031\n\005range\030\002 \001(\0132\n" +
+      ".TextRange\022\026\n\016other_file_key\030\003 \001(\t\"Q\n\013Du" +
+      "plication\022#\n\017origin_position\030\001 \001(\0132\n.Tex" +
+      "tRange\022\035\n\tduplicate\030\002 \003(\0132\n.Duplicate\"[\n" +
+      "\tTextRange\022\022\n\nstart_line\030\001 \001(\005\022\020\n\010end_li",
+      "ne\030\002 \001(\005\022\024\n\014start_offset\030\003 \001(\005\022\022\n\nend_of" +
+      "fset\030\004 \001(\005\"H\n\006Symbol\022\037\n\013declaration\030\001 \001(" +
+      "\0132\n.TextRange\022\035\n\treference\030\002 \003(\0132\n.TextR" +
+      "ange\"\260\001\n\010Coverage\022\014\n\004line\030\001 \001(\005\022\022\n\ncondi" +
+      "tions\030\002 \001(\005\022\017\n\007ut_hits\030\003 \001(\010\022\017\n\007it_hits\030" +
+      "\004 \001(\010\022\035\n\025ut_covered_conditions\030\005 \001(\005\022\035\n\025" +
+      "it_covered_conditions\030\006 \001(\005\022\"\n\032overall_c" +
+      "overed_conditions\030\007 \001(\005\"P\n\022SyntaxHighlig" +
+      "hting\022\031\n\005range\030\001 \001(\0132\n.TextRange\022\037\n\004type" +
+      "\030\002 \001(\0162\021.HighlightingType\"j\n\004Test\022\014\n\004nam",
+      "e\030\001 \001(\t\022\033\n\006status\030\002 \001(\0162\013.TestStatus\022\026\n\016" +
+      "duration_in_ms\030\003 \001(\003\022\022\n\nstacktrace\030\004 \001(\t" +
+      "\022\013\n\003msg\030\005 \001(\t\"\221\001\n\016CoverageDetail\022\021\n\ttest" +
+      "_name\030\001 \001(\t\0221\n\014covered_file\030\002 \003(\0132\033.Cove" +
+      "rageDetail.CoveredFile\0329\n\013CoveredFile\022\020\n" +
+      "\010file_ref\030\001 \001(\005\022\030\n\014covered_line\030\002 \003(\005B\002\020" +
+      "\001B#\n\037org.sonar.batch.protocol.outputH\001"
     };
     com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
         new com.google.protobuf.Descriptors.FileDescriptor.    InternalDescriptorAssigner() {
@@ -19193,7 +19383,7 @@ public final class BatchReport {
     internal_static_Issue_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessage.FieldAccessorTable(
         internal_static_Issue_descriptor,
-        new java.lang.String[] { "RuleRepository", "RuleKey", "Line", "Msg", "Severity", "EffortToFix", "Attributes", "Locations", "ExecutionFlows", });
+        new java.lang.String[] { "RuleRepository", "RuleKey", "Line", "Msg", "Severity", "EffortToFix", "Attributes", "PrimaryLocation", "AdditionalLocation", "ExecutionFlow", });
     internal_static_IssueLocation_descriptor =
       getDescriptor().getMessageTypes().get(6);
     internal_static_IssueLocation_fieldAccessorTable = new
@@ -19205,7 +19395,7 @@ public final class BatchReport {
     internal_static_ExecutionFlow_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessage.FieldAccessorTable(
         internal_static_ExecutionFlow_descriptor,
-        new java.lang.String[] { "Locations", });
+        new java.lang.String[] { "Location", });
     internal_static_Changesets_descriptor =
       getDescriptor().getMessageTypes().get(8);
     internal_static_Changesets_fieldAccessorTable = new
index 76eec978266a8bda7182ea05e0902b71f6f794ce..21fe15c29557578c1058fcab72b33898771fcf32 100644 (file)
@@ -103,8 +103,9 @@ message Issue {
   optional Severity severity = 5;
   optional double effort_to_fix = 6;
   optional string attributes = 7;
-  repeated IssueLocation locations = 8;
-  repeated ExecutionFlow execution_flows = 9;
+  optional IssueLocation primary_location = 9;
+  repeated IssueLocation additional_location = 10;
+  repeated ExecutionFlow execution_flow = 11;
 }
 
 message IssueLocation {
@@ -115,7 +116,7 @@ message IssueLocation {
 }
 
 message ExecutionFlow {
-  repeated IssueLocation locations = 1;
+  repeated IssueLocation location = 1;
 }
 
 message Changesets {
index 4cbadbe8668b2e27c6d6b15eca801459092ac581..069c37290fa83a9e72a8599cf71b4d3ff6163dfb 100644 (file)
@@ -23,9 +23,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.fs.InputPath;
-import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.ResourceUtils;
 
@@ -35,7 +33,7 @@ public class BatchComponent {
   private final Resource r;
   private final BatchComponent parent;
   private final Collection<BatchComponent> children = new ArrayList<>();
-  private InputPath inputPath;
+  private InputComponent inputComponent;
 
   public BatchComponent(int batchId, Resource r, @Nullable BatchComponent parent) {
     this.batchId = batchId;
@@ -68,21 +66,16 @@ public class BatchComponent {
   }
 
   public boolean isFile() {
-    return Qualifiers.isFile(r) || StringUtils.equals(Qualifiers.UNIT_TEST_FILE, r.getQualifier());
+    return this.inputComponent.isFile();
   }
 
-  public boolean isDir() {
-    return Qualifiers.isDirectory(r);
-  }
-
-  public BatchComponent setInputPath(InputPath inputPath) {
-    this.inputPath = inputPath;
+  public BatchComponent setInputComponent(InputComponent inputComponent) {
+    this.inputComponent = inputComponent;
     return this;
   }
 
-  @CheckForNull
-  public InputPath inputPath() {
-    return inputPath;
+  public InputComponent inputComponent() {
+    return inputComponent;
   }
 
   public boolean isProjectOrModule() {
index 88f0d47ba0eecb6d3690e03d71b9da2b547c45da..c8385f5345a87c8992195e8cdcb8d33eaebf45c8 100644 (file)
@@ -37,6 +37,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.SonarIndex;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.measure.MetricFinder;
 import org.sonar.api.design.Dependency;
 import org.sonar.api.measures.Measure;
@@ -82,7 +83,8 @@ public class DefaultIndex extends SonarIndex {
   void doStart(Project rootProject) {
     Bucket bucket = new Bucket(rootProject);
     addBucket(rootProject, bucket);
-    componentCache.add(rootProject, null);
+    BatchComponent component = componentCache.add(rootProject, null);
+    component.setInputComponent(new DefaultInputModule(rootProject.getEffectiveKey()));
     currentProject = rootProject;
 
     for (Project module : rootProject.getModules()) {
@@ -269,11 +271,7 @@ public class DefaultIndex extends SonarIndex {
       return null;
     }
 
-    Resource parent = null;
-    if (!ResourceUtils.isLibrary(resource)) {
-      // a library has no parent
-      parent = (Resource) ObjectUtils.defaultIfNull(parentReference, currentProject);
-    }
+    Resource parent = (Resource) ObjectUtils.defaultIfNull(parentReference, currentProject);
 
     Bucket parentBucket = getBucket(parent);
     if (parentBucket == null && parent != null) {
@@ -290,7 +288,10 @@ public class DefaultIndex extends SonarIndex {
     addBucket(resource, bucket);
 
     Resource parentResource = parentBucket != null ? parentBucket.getResource() : null;
-    componentCache.add(resource, parentResource);
+    BatchComponent component = componentCache.add(resource, parentResource);
+    if (ResourceUtils.isProject(resource)) {
+      component.setInputComponent(new DefaultInputModule(resource.getEffectiveKey()));
+    }
 
     return bucket;
   }
index c2639e7cfbaf66c78a09cf27e1f4442acd06dc81..d6e5ca2f14f561a9b4f8293e9b10c20b2c83b6a0 100644 (file)
@@ -43,7 +43,7 @@ public class DefaultIssuable implements Issuable {
   @Override
   public IssueBuilder newIssueBuilder() {
     DefaultIssue newIssue = (DefaultIssue) sensorContext.newIssue();
-    return new DeprecatedIssueBuilderWrapper(component, newIssue);
+    return new DeprecatedIssueBuilderWrapper(component.inputComponent(), newIssue);
   }
 
   @Override
index ae85a999274025d5a15a1aa0be69f0e5d685035f..146a40e06e31591aa1fd509cba1e8e158d665dbf 100644 (file)
  */
 package org.sonar.batch.issue;
 
-import java.util.ArrayList;
-import java.util.List;
+import com.google.common.base.Preconditions;
 import javax.annotation.Nullable;
-import org.sonar.api.batch.fs.InputDir;
+import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.TextRange;
 import org.sonar.api.batch.rule.Severity;
@@ -33,17 +32,15 @@ import org.sonar.api.issue.Issuable;
 import org.sonar.api.issue.Issuable.IssueBuilder;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.rule.RuleKey;
-import org.sonar.batch.index.BatchComponent;
 
 public class DeprecatedIssueBuilderWrapper implements Issuable.IssueBuilder {
 
   private final DefaultIssue newIssue;
-  private final BatchComponent primaryComponent;
+  private final InputComponent primaryComponent;
   private TextRange primaryRange = null;
   private String primaryMessage = null;
-  private List<NewIssueLocation> locations = new ArrayList<>();
 
-  public DeprecatedIssueBuilderWrapper(BatchComponent primaryComponent, DefaultIssue newIssue) {
+  public DeprecatedIssueBuilderWrapper(InputComponent primaryComponent, DefaultIssue newIssue) {
     this.primaryComponent = primaryComponent;
     this.newIssue = newIssue;
   }
@@ -56,9 +53,10 @@ public class DeprecatedIssueBuilderWrapper implements Issuable.IssueBuilder {
 
   @Override
   public IssueBuilder line(@Nullable Integer line) {
+    Preconditions.checkState(newIssue.primaryLocation() == null, "Do not use line() and at() for the same issue");
     if (primaryComponent.isFile()) {
       if (line != null) {
-        this.primaryRange = ((InputFile) primaryComponent.inputPath()).selectLine(line.intValue());
+        this.primaryRange = ((InputFile) primaryComponent).selectLine(line.intValue());
       }
       return this;
     } else {
@@ -68,6 +66,7 @@ public class DeprecatedIssueBuilderWrapper implements Issuable.IssueBuilder {
 
   @Override
   public IssueBuilder message(String message) {
+    Preconditions.checkState(newIssue.primaryLocation() == null, "Do not use message() and at() for the same issue");
     this.primaryMessage = message;
     return this;
   }
@@ -77,9 +76,16 @@ public class DeprecatedIssueBuilderWrapper implements Issuable.IssueBuilder {
     return new DefaultIssueLocation();
   }
 
+  @Override
+  public IssueBuilder at(NewIssueLocation primaryLocation) {
+    Preconditions.checkState(primaryMessage == null && primaryRange == null, "Do not use message() or line() and at() for the same issue");
+    newIssue.at(primaryLocation);
+    return this;
+  }
+
   @Override
   public IssueBuilder addLocation(NewIssueLocation location) {
-    locations.add(location);
+    newIssue.addLocation(location);
     return this;
   }
 
@@ -113,27 +119,16 @@ public class DeprecatedIssueBuilderWrapper implements Issuable.IssueBuilder {
 
   @Override
   public Issue build() {
-    if (primaryMessage != null || primaryRange != null || locations.isEmpty()) {
-      NewIssueLocation newLocation = newIssue.newLocation();
+    if (newIssue.primaryLocation() == null) {
+      NewIssueLocation newLocation = newIssue.newLocation().on(primaryComponent);
       if (primaryMessage != null) {
         newLocation.message(primaryMessage);
       }
-      if (primaryComponent.isProjectOrModule()) {
-        newLocation.onProject();
-      } else if (primaryComponent.isFile()) {
-        newLocation.onFile((InputFile) primaryComponent.inputPath());
-        if (primaryRange != null) {
-          newLocation.at(primaryRange);
-        }
-      } else if (primaryComponent.isDir()) {
-        newLocation.onDir((InputDir) primaryComponent.inputPath());
+      if (primaryComponent.isFile() && primaryRange != null) {
+        newLocation.at(primaryRange);
       }
-      newIssue.addLocation(newLocation);
-    }
-    for (NewIssueLocation issueLocation : locations) {
-      newIssue.addLocation(issueLocation);
+      newIssue.at(newLocation);
     }
-
     return new DeprecatedIssueWrapper(newIssue);
   }
 
index e2773e086583040072e727155a6f806be196cbf7..752e1e92d2fb660a7a03dc7c120f20da47a990f6 100644 (file)
@@ -45,7 +45,7 @@ public class DeprecatedIssueWrapper implements Issue {
 
   @Override
   public String key() {
-    return newIssue.key();
+    return null;
   }
 
   @Override
index 203e495d01a14aa78f9f5b6c943bb2cccbc6b113..ed609db03704ad4866e38b6ba52c657944f27b74 100644 (file)
  */
 package org.sonar.batch.issue;
 
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
 import org.sonar.api.batch.BatchSide;
 import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.IssueComment;
 import org.sonar.api.issue.batch.IssueFilter;
+import org.sonar.api.resources.Project;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.batch.protocol.output.BatchReport;
 
 @BatchSide
 public class IssueFilters {
 
   private final org.sonar.api.issue.IssueFilter[] exclusionFilters;
   private final IssueFilter[] filters;
+  private final Project project;
 
-  public IssueFilters(org.sonar.api.issue.IssueFilter[] exclusionFilters, IssueFilter[] filters) {
+  public IssueFilters(Project project, org.sonar.api.issue.IssueFilter[] exclusionFilters, IssueFilter[] filters) {
+    this.project = project;
     this.exclusionFilters = exclusionFilters;
     this.filters = filters;
   }
 
-  public IssueFilters(org.sonar.api.issue.IssueFilter[] exclusionFilters) {
-    this(exclusionFilters, new IssueFilter[0]);
+  public IssueFilters(Project project, org.sonar.api.issue.IssueFilter[] exclusionFilters) {
+    this(project, exclusionFilters, new IssueFilter[0]);
   }
 
-  public IssueFilters(IssueFilter[] filters) {
-    this(new org.sonar.api.issue.IssueFilter[0], filters);
+  public IssueFilters(Project project, IssueFilter[] filters) {
+    this(project, new org.sonar.api.issue.IssueFilter[0], filters);
   }
 
-  public IssueFilters() {
-    this(new org.sonar.api.issue.IssueFilter[0], new IssueFilter[0]);
+  public IssueFilters(Project project) {
+    this(project, new org.sonar.api.issue.IssueFilter[0], new IssueFilter[0]);
   }
 
-  public boolean accept(Issue issue) {
+  public boolean accept(String componentKey, BatchReport.Issue rawIssue) {
+    Issue issue = toIssueForIssueFilter(componentKey, rawIssue);
     if (new DefaultIssueFilterChain(filters).accept(issue)) {
       // Apply deprecated rules only if filter chain accepts the current issue
       for (org.sonar.api.issue.IssueFilter filter : exclusionFilters) {
@@ -59,4 +73,145 @@ public class IssueFilters {
       return false;
     }
   }
+
+  private Issue toIssueForIssueFilter(final String componentKey, final BatchReport.Issue rawIssue) {
+    return new Issue() {
+
+      @Override
+      public String key() {
+        throw unsupported();
+      }
+
+      @Override
+      public String componentKey() {
+        return componentKey;
+      }
+
+      @Override
+      public RuleKey ruleKey() {
+        return RuleKey.of(rawIssue.getRuleRepository(), rawIssue.getRuleKey());
+      }
+
+      @Override
+      public String language() {
+        throw unsupported();
+      }
+
+      @Override
+      public String severity() {
+        return rawIssue.getSeverity().name();
+      }
+
+      @Override
+      public String message() {
+        return rawIssue.getMsg();
+      }
+
+      @Override
+      public Integer line() {
+        return rawIssue.hasLine() ? rawIssue.getLine() : null;
+      }
+
+      @Override
+      public Double effortToFix() {
+        return rawIssue.hasEffortToFix() ? rawIssue.getEffortToFix() : null;
+      }
+
+      @Override
+      public String status() {
+        return Issue.STATUS_OPEN;
+      }
+
+      @Override
+      public String resolution() {
+        return null;
+      }
+
+      @Override
+      public String reporter() {
+        throw unsupported();
+      }
+
+      @Override
+      public String assignee() {
+        return null;
+      }
+
+      @Override
+      public Date creationDate() {
+        return project.getAnalysisDate();
+      }
+
+      @Override
+      public Date updateDate() {
+        return null;
+      }
+
+      @Override
+      public Date closeDate() {
+        return null;
+      }
+
+      @Override
+      public String attribute(String key) {
+        return attributes().get(key);
+      }
+
+      @Override
+      public Map<String, String> attributes() {
+        return rawIssue.hasAttributes() ? KeyValueFormat.parse(rawIssue.getAttributes()) : Collections.<String, String>emptyMap();
+      }
+
+      @Override
+      public String authorLogin() {
+        throw unsupported();
+      }
+
+      @Override
+      public String actionPlanKey() {
+        throw unsupported();
+      }
+
+      @Override
+      public List<IssueComment> comments() {
+        throw unsupported();
+      }
+
+      @Override
+      public boolean isNew() {
+        throw unsupported();
+      }
+
+      @Override
+      public Duration debt() {
+        throw unsupported();
+      }
+
+      @Override
+      public String projectKey() {
+        return project.getEffectiveKey();
+      }
+
+      @Override
+      public String projectUuid() {
+        throw unsupported();
+      }
+
+      @Override
+      public String componentUuid() {
+        throw unsupported();
+      }
+
+      @Override
+      public Collection<String> tags() {
+        throw unsupported();
+      }
+
+      private UnsupportedOperationException unsupported() {
+        return new UnsupportedOperationException("Not available for issues filters");
+      }
+
+    };
+
+  }
 }
index d74fef6dcb07572416805b61cf55da2fb96ec0c2..67d3c98bb9438742fcee0a848b47c3742ce62aa6 100644 (file)
 package org.sonar.batch.issue;
 
 import com.google.common.base.Strings;
-import javax.annotation.Nullable;
-import org.sonar.api.batch.fs.InputPath;
 import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.DefaultInputComponent;
 import org.sonar.api.batch.rule.ActiveRule;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.rule.Rule;
 import org.sonar.api.batch.rule.Rules;
-import org.sonar.api.batch.rule.Severity;
 import org.sonar.api.batch.sensor.issue.Issue;
-import org.sonar.api.resources.Project;
+import org.sonar.api.batch.sensor.issue.Issue.ExecutionFlow;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.api.utils.MessageException;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.index.BatchComponentCache;
-import org.sonar.batch.protocol.Constants;
+import org.sonar.batch.protocol.Constants.Severity;
 import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.protocol.output.BatchReport.IssueLocation;
+import org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder;
 import org.sonar.batch.report.ReportPublisher;
-import org.sonar.core.issue.DefaultIssue;
 
 /**
  * Initialize the issues raised during scan.
@@ -47,108 +46,126 @@ public class ModuleIssues {
 
   private final ActiveRules activeRules;
   private final Rules rules;
-  private final Project project;
   private final IssueFilters filters;
   private final ReportPublisher reportPublisher;
   private final BatchComponentCache componentCache;
   private final BatchReport.Issue.Builder builder = BatchReport.Issue.newBuilder();
+  private final Builder locationBuilder = IssueLocation.newBuilder();
+  private final org.sonar.batch.protocol.output.BatchReport.TextRange.Builder textRangeBuilder = org.sonar.batch.protocol.output.BatchReport.TextRange.newBuilder();
+  private final BatchReport.ExecutionFlow.Builder flowBuilder = BatchReport.ExecutionFlow.newBuilder();
 
-  public ModuleIssues(ActiveRules activeRules, Rules rules, Project project, IssueFilters filters, ReportPublisher reportPublisher, BatchComponentCache componentCache) {
+  public ModuleIssues(ActiveRules activeRules, Rules rules, IssueFilters filters, ReportPublisher reportPublisher, BatchComponentCache componentCache) {
     this.activeRules = activeRules;
     this.rules = rules;
-    this.project = project;
     this.filters = filters;
     this.reportPublisher = reportPublisher;
     this.componentCache = componentCache;
   }
 
   public boolean initAndAddIssue(Issue issue) {
-    BatchComponent component;
-    InputPath inputPath = issue.locations().get(0).inputPath();
-    if (inputPath != null) {
-      component = componentCache.get(inputPath);
-    } else {
-      component = componentCache.get(project);
-    }
-    DefaultIssue defaultIssue = toDefaultIssue(project.getKey(), component.key(), issue);
-    RuleKey ruleKey = defaultIssue.ruleKey();
-    Rule rule = rules.find(ruleKey);
-    validateRule(defaultIssue, rule);
-    ActiveRule activeRule = activeRules.find(ruleKey);
+    String key = ((DefaultInputComponent) issue.primaryLocation().inputComponent()).key();
+    BatchComponent component = componentCache.get(key);
+
+    Rule rule = validateRule(issue);
+    ActiveRule activeRule = activeRules.find(issue.ruleKey());
     if (activeRule == null) {
       // rule does not exist or is not enabled -> ignore the issue
       return false;
     }
-    updateIssue(defaultIssue, rule, activeRule);
-    if (filters.accept(defaultIssue)) {
-      write(component, defaultIssue);
-      return true;
-    }
-    return false;
-  }
 
-  public void write(BatchComponent component, DefaultIssue issue) {
-    reportPublisher.getWriter().appendComponentIssue(component.batchId(), toReportIssue(builder, issue));
-  }
+    String primaryMessage = Strings.isNullOrEmpty(issue.primaryLocation().message()) ? rule.name() : issue.primaryLocation().message();
+    org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity();
+    Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRule.severity());
 
-  private static BatchReport.Issue toReportIssue(BatchReport.Issue.Builder builder, DefaultIssue issue) {
     builder.clear();
+    locationBuilder.clear();
     // non-null fields
-    builder.setSeverity(Constants.Severity.valueOf(issue.severity()));
+    builder.setSeverity(severity);
     builder.setRuleRepository(issue.ruleKey().repository());
     builder.setRuleKey(issue.ruleKey().rule());
     builder.setAttributes(KeyValueFormat.format(issue.attributes()));
+    builder.setMsg(primaryMessage);
+    locationBuilder.setMsg(primaryMessage);
 
-    // nullable fields
-    Integer line = issue.line();
-    if (line != null) {
-      builder.setLine(line);
-    }
-    String message = issue.message();
-    if (message != null) {
-      builder.setMsg(message);
+    locationBuilder.setComponentRef(component.batchId());
+    TextRange primaryTextRange = issue.primaryLocation().textRange();
+    applyTextRange(primaryTextRange);
+    if (primaryTextRange != null) {
+      builder.setLine(primaryTextRange.start().line());
     }
+    builder.setPrimaryLocation(locationBuilder.build());
     Double effortToFix = issue.effortToFix();
     if (effortToFix != null) {
       builder.setEffortToFix(effortToFix);
     }
-    return builder.build();
+    applyAdditionalLocations(issue);
+    applyExecutionFlows(issue);
+    BatchReport.Issue rawIssue = builder.build();
+
+    if (filters.accept(key, rawIssue)) {
+      write(component, rawIssue);
+      return true;
+    }
+    return false;
   }
 
-  public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) {
-    Severity overriddenSeverity = issue.overriddenSeverity();
-    TextRange textRange = issue.locations().get(0).textRange();
-    return new org.sonar.core.issue.DefaultIssueBuilder()
-      .componentKey(componentKey)
-      .projectKey(projectKey)
-      .ruleKey(RuleKey.of(issue.ruleKey().repository(), issue.ruleKey().rule()))
-      .effortToFix(issue.effortToFix())
-      .line(textRange != null ? textRange.start().line() : null)
-      .message(issue.locations().get(0).message())
-      .severity(overriddenSeverity != null ? overriddenSeverity.name() : null)
-      .build();
+  private void applyAdditionalLocations(Issue issue) {
+    for (org.sonar.api.batch.sensor.issue.IssueLocation additionalLocation : issue.locations()) {
+      locationBuilder.clear();
+      String locationComponentKey = ((DefaultInputComponent) additionalLocation.inputComponent()).key();
+      locationBuilder.setComponentRef(componentCache.get(locationComponentKey).batchId());
+      String message = additionalLocation.message();
+      if (message != null) {
+        locationBuilder.setMsg(message);
+      }
+      applyTextRange(additionalLocation.textRange());
+      builder.addAdditionalLocation(locationBuilder.build());
+    }
+  }
+
+  private void applyExecutionFlows(Issue issue) {
+    for (ExecutionFlow executionFlow : issue.executionFlows()) {
+      flowBuilder.clear();
+      for (org.sonar.api.batch.sensor.issue.IssueLocation location : executionFlow.locations()) {
+        locationBuilder.clear();
+        String locationComponentKey = ((DefaultInputComponent) location.inputComponent()).key();
+        locationBuilder.setComponentRef(componentCache.get(locationComponentKey).batchId());
+        String message = location.message();
+        if (message != null) {
+          locationBuilder.setMsg(message);
+        }
+        applyTextRange(location.textRange());
+        flowBuilder.addLocation(locationBuilder.build());
+      }
+      builder.addExecutionFlow(flowBuilder.build());
+    }
+  }
+
+  private void applyTextRange(TextRange primaryTextRange) {
+    if (primaryTextRange != null) {
+      textRangeBuilder.clear();
+      textRangeBuilder.setStartLine(primaryTextRange.start().line());
+      textRangeBuilder.setStartOffset(primaryTextRange.start().lineOffset());
+      textRangeBuilder.setEndLine(primaryTextRange.end().line());
+      textRangeBuilder.setEndOffset(primaryTextRange.end().lineOffset());
+      locationBuilder.setTextRange(textRangeBuilder.build());
+    }
   }
 
-  private static void validateRule(DefaultIssue issue, @Nullable Rule rule) {
+  private Rule validateRule(Issue issue) {
     RuleKey ruleKey = issue.ruleKey();
+    Rule rule = rules.find(ruleKey);
     if (rule == null) {
       throw MessageException.of(String.format("The rule '%s' does not exist.", ruleKey));
     }
-    if (Strings.isNullOrEmpty(rule.name()) && Strings.isNullOrEmpty(issue.message())) {
+    if (Strings.isNullOrEmpty(rule.name()) && Strings.isNullOrEmpty(issue.primaryLocation().message())) {
       throw MessageException.of(String.format("The rule '%s' has no name and the related issue has no message.", ruleKey));
     }
+    return rule;
   }
 
-  private void updateIssue(DefaultIssue issue, @Nullable Rule rule, ActiveRule activeRule) {
-    if (Strings.isNullOrEmpty(issue.message())) {
-      issue.setMessage(rule.name());
-    }
-    if (project != null) {
-      issue.setCreationDate(project.getAnalysisDate());
-      issue.setUpdateDate(project.getAnalysisDate());
-    }
-    if (issue.severity() == null) {
-      issue.setSeverity(activeRule.severity());
-    }
+  public void write(BatchComponent component, BatchReport.Issue rawIssue) {
+    reportPublisher.getWriter().appendComponentIssue(component.batchId(), rawIssue);
   }
+
 }
index ee316017c2157bb3177f095ef56563dc0eacd9f1..8fcbe931c362a73e364f9faaecc4812d74088b14 100644 (file)
@@ -48,7 +48,6 @@ import org.sonar.batch.protocol.input.ProjectRepositories;
 import org.sonar.batch.protocol.output.BatchReport;
 import org.sonar.batch.protocol.output.BatchReportReader;
 import org.sonar.batch.report.ReportPublisher;
-import org.sonar.batch.scan.filesystem.InputPathCache;
 import org.sonar.core.component.ComponentKeys;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
@@ -68,7 +67,6 @@ public class LocalIssueTracking {
   private final IssueUpdater updater;
   private final IssueChangeContext changeContext;
   private final ActiveRules activeRules;
-  private final InputPathCache inputPathCache;
   private final BatchComponentCache componentCache;
   private final ServerIssueRepository serverIssueRepository;
   private final ProjectRepositories projectRepositories;
@@ -78,7 +76,7 @@ public class LocalIssueTracking {
 
   public LocalIssueTracking(BatchComponentCache resourceCache, IssueCache issueCache, IssueTracking tracking,
     ServerLineHashesLoader lastLineHashes, IssueWorkflow workflow, IssueUpdater updater,
-    ActiveRules activeRules, InputPathCache inputPathCache, ServerIssueRepository serverIssueRepository,
+    ActiveRules activeRules, ServerIssueRepository serverIssueRepository,
     ProjectRepositories projectRepositories, AnalysisMode analysisMode, ReportPublisher reportPublisher) {
     this.componentCache = resourceCache;
     this.issueCache = issueCache;
@@ -86,7 +84,6 @@ public class LocalIssueTracking {
     this.lastLineHashes = lastLineHashes;
     this.workflow = workflow;
     this.updater = updater;
-    this.inputPathCache = inputPathCache;
     this.serverIssueRepository = serverIssueRepository;
     this.projectRepositories = projectRepositories;
     this.analysisMode = analysisMode;
@@ -183,7 +180,7 @@ public class LocalIssueTracking {
   private SourceHashHolder loadSourceHashes(BatchComponent component) {
     SourceHashHolder sourceHashHolder = null;
     if (component.isFile()) {
-      DefaultInputFile file = (DefaultInputFile) inputPathCache.getInputPath(component);
+      DefaultInputFile file = (DefaultInputFile) component.inputComponent();
       if (file == null) {
         throw new IllegalStateException("Resource " + component.resource() + " was not found in InputPath cache");
       }
index bcf8fe66538d33eade8c44d0031f665d0457b75d..7b5aa5faee83110a609527f5ebc7bce1caa6e521 100644 (file)
@@ -99,7 +99,7 @@ public class ServerIssueRepository {
       if (!component.isFile()) {
         throw new UnsupportedOperationException("Incremental mode should only get issues on files");
       }
-      InputFile inputFile = (InputFile) inputPathCache.getInputPath(component);
+      InputFile inputFile = (InputFile) component.inputComponent();
       if (inputFile.status() == Status.ADDED) {
         return Collections.emptyList();
       }
index 1b02c9090d54f419cf8575ec8c178fabc27fe9e3..580eb12d2a29af48a68662066a940e0ddf9bd8f7 100644 (file)
@@ -140,7 +140,7 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
     return result;
   }
 
-  private String key(InputPath inputPath) {
+  private static String key(InputPath inputPath) {
     return inputPath instanceof InputFile ? ((DefaultInputFile) inputPath).key() : ((DefaultInputDir) inputPath).key();
   }
 
index 7b1edddabecbbb60234254bcc6e41fa2bf2f49dd..38787dea74cae37ce9be34ebeeff46de52b696b4 100644 (file)
@@ -22,19 +22,18 @@ package org.sonar.batch.postjob;
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
+import javax.annotation.Nullable;
 import org.sonar.api.batch.AnalysisMode;
-import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.batch.postjob.PostJobContext;
 import org.sonar.api.batch.postjob.issue.Issue;
 import org.sonar.api.batch.rule.Severity;
 import org.sonar.api.config.Settings;
-import org.sonar.core.issue.DefaultIssue;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.issue.IssueCache;
-
-import javax.annotation.Nullable;
+import org.sonar.core.issue.DefaultIssue;
 
 public class DefaultPostJobContext implements PostJobContext {
 
@@ -104,9 +103,9 @@ public class DefaultPostJobContext implements PostJobContext {
     }
 
     @Override
-    public InputPath inputPath() {
+    public InputComponent inputComponent() {
       BatchComponent component = resourceCache.get(wrapped.componentKey());
-      return component != null ? component.inputPath() : null;
+      return component != null ? component.inputComponent() : null;
     }
 
     @Override
index e3c971fd10c29a4e607a6f4196fc15fb6d25d06f..e8d11a2d775c80bfe1caab42db1ad9abc7d2482e 100644 (file)
@@ -74,7 +74,7 @@ public class ComponentsPublisher implements ReportPublisherStep {
 
     if (batchComponent.isFile()) {
       builder.setIsTest(ResourceUtils.isUnitTestFile(r));
-      builder.setLines(((InputFile) batchComponent.inputPath()).lines());
+      builder.setLines(((InputFile) batchComponent.inputComponent()).lines());
     }
     String name = getName(r);
     if (name != null) {
index 2e41b0401fa2823b05210deae973b09af81a38db..4b13af12afcf57ef92ca36ed7ab2e40f0aaf674d 100644 (file)
@@ -54,42 +54,48 @@ public class CoveragePublisher implements ReportPublisherStep {
       }
       Map<Integer, Coverage.Builder> coveragePerLine = new LinkedHashMap<>();
 
-      applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine, new MeasureOperation() {
-        @Override
-        public void apply(String value, Coverage.Builder builder) {
-          builder.setUtHits(Integer.parseInt(value) > 0);
-        }
-      });
-      applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.CONDITIONS_BY_LINE_KEY, coveragePerLine, new MeasureOperation() {
-        @Override
-        public void apply(String value, Coverage.Builder builder) {
-          builder.setConditions(Integer.parseInt(value));
-        }
-      });
-      applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, new MeasureOperation() {
-        @Override
-        public void apply(String value, Coverage.Builder builder) {
-          builder.setUtCoveredConditions(Integer.parseInt(value));
-        }
-      });
-      applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine, new MeasureOperation() {
-        @Override
-        public void apply(String value, Coverage.Builder builder) {
-          builder.setItHits(Integer.parseInt(value) > 0);
-        }
-      });
-      applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, new MeasureOperation() {
-        @Override
-        public void apply(String value, Coverage.Builder builder) {
-          builder.setItCoveredConditions(Integer.parseInt(value));
-        }
-      });
-      applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, new MeasureOperation() {
-        @Override
-        public void apply(String value, Coverage.Builder builder) {
-          builder.setOverallCoveredConditions(Integer.parseInt(value));
-        }
-      });
+      applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine,
+        new MeasureOperation() {
+          @Override
+          public void apply(String value, Coverage.Builder builder) {
+            builder.setUtHits(Integer.parseInt(value) > 0);
+          }
+        });
+      applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.CONDITIONS_BY_LINE_KEY, coveragePerLine,
+        new MeasureOperation() {
+          @Override
+          public void apply(String value, Coverage.Builder builder) {
+            builder.setConditions(Integer.parseInt(value));
+          }
+        });
+      applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine,
+        new MeasureOperation() {
+          @Override
+          public void apply(String value, Coverage.Builder builder) {
+            builder.setUtCoveredConditions(Integer.parseInt(value));
+          }
+        });
+      applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine,
+        new MeasureOperation() {
+          @Override
+          public void apply(String value, Coverage.Builder builder) {
+            builder.setItHits(Integer.parseInt(value) > 0);
+          }
+        });
+      applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine,
+        new MeasureOperation() {
+          @Override
+          public void apply(String value, Coverage.Builder builder) {
+            builder.setItCoveredConditions(Integer.parseInt(value));
+          }
+        });
+      applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine,
+        new MeasureOperation() {
+          @Override
+          public void apply(String value, Coverage.Builder builder) {
+            builder.setOverallCoveredConditions(Integer.parseInt(value));
+          }
+        });
 
       writer.writeComponentCoverage(resource.batchId(), Iterables.transform(coveragePerLine.values(), BuildCoverage.INSTANCE));
     }
@@ -122,7 +128,7 @@ public class CoveragePublisher implements ReportPublisherStep {
     void apply(String value, Coverage.Builder builder);
   }
 
-  private enum BuildCoverage implements Function<Coverage.Builder, Coverage>{
+  private enum BuildCoverage implements Function<Coverage.Builder, Coverage> {
     INSTANCE;
 
     @Override
index c4395e755e96ff40029692a138bbf05a7b19a78b..68a5bee423f2ad18672c5e7bfd03e0b48b8379ed 100644 (file)
@@ -50,7 +50,7 @@ public class SourcePublisher implements ReportPublisherStep {
         continue;
       }
 
-      DefaultInputFile inputFile = (DefaultInputFile) resource.inputPath();
+      DefaultInputFile inputFile = (DefaultInputFile) resource.inputComponent();
       File iofile = writer.getSourceFile(resource.batchId());
       int line = 0;
       try (FileOutputStream output = new FileOutputStream(iofile); BOMInputStream bomIn = new BOMInputStream(new FileInputStream(inputFile.file()),
index fd430aebc582b8384f8fa8e843bfa0ec83c94802..c249d8e374036746c80ea2951967307fede0e1f8 100644 (file)
@@ -119,7 +119,7 @@ public class TestExecutionAndCoveragePublisher implements ReportPublisherStep {
         continue;
       }
 
-      DefaultInputFile inputFile = (DefaultInputFile) component.inputPath();
+      DefaultInputFile inputFile = (DefaultInputFile) component.inputComponent();
       if (inputFile.type() != Type.TEST) {
         continue;
       }
index 5e79960365c8d7197734bed8b34781515a3aebb2..e4e5a1602ee1843ed7bec9db1b7a9e83d4f3473b 100644 (file)
@@ -59,10 +59,10 @@ public class ComponentIndexer {
       Resource sonarFile = File.create(inputFile.relativePath(), languages.get(languageKey), unitTest);
       sonarIndex.index(sonarFile);
       BatchComponent file = componentCache.get(sonarFile);
-      file.setInputPath(inputFile);
+      file.setInputComponent(inputFile);
       Resource sonarDir = file.parent().resource();
       InputDir inputDir = fs.inputDir(inputFile.file().getParentFile());
-      componentCache.get(sonarDir).setInputPath(inputDir);
+      componentCache.get(sonarDir).setInputComponent(inputDir);
     }
   }
 }
index cbfd8d0c5df883e7dbfe03b3f10790bfa0f04695..3164a69d10af3616b72477641af4ff2fcc38ebbe 100644 (file)
@@ -21,20 +21,16 @@ package org.sonar.batch.scan.filesystem;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Iterables;
-import org.sonar.api.batch.BatchSide;
-import org.sonar.api.batch.fs.InputDir;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputPath;
-import org.sonar.batch.index.BatchComponent;
-
-import javax.annotation.CheckForNull;
-
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
+import javax.annotation.CheckForNull;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.fs.InputDir;
+import org.sonar.api.batch.fs.InputFile;
 
 /**
  * Cache of all files and dirs. This cache is shared amongst all project modules. Inclusion and
@@ -130,14 +126,4 @@ public class InputPathCache {
     return null;
   }
 
-  @CheckForNull
-  public InputPath getInputPath(BatchComponent component) {
-    if (component.isFile()) {
-      return getFile(component.parent().parent().resource().getEffectiveKey(), component.resource().getPath());
-    } else if (component.isDir()) {
-      return getDir(component.parent().parent().resource().getEffectiveKey(), component.resource().getPath());
-    }
-    return null;
-  }
-
 }
index 9ae89557e953116f742cb8e1f7e292ca91447539..dd7d4631138bb9100d3131978d2fb298c043e7d4 100644 (file)
  */
 package org.sonar.batch.scan.report;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang.StringEscapeUtils;
 import org.slf4j.Logger;
@@ -29,11 +33,6 @@ import org.sonar.api.batch.fs.InputFile;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.scan.filesystem.InputPathCache;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
 @BatchSide
 public class SourceProvider {
 
@@ -52,7 +51,7 @@ public class SourceProvider {
       return Collections.emptyList();
     }
     try {
-      InputFile inputFile = (InputFile) inputPathCache.getInputPath(component);
+      InputFile inputFile = (InputFile) component.inputComponent();
       List<String> lines = FileUtils.readLines(inputFile.file(), fs.encoding());
       List<String> escapedLines = new ArrayList<>(lines.size());
       for (String line : lines) {
index 2a1a7d3ac5e6faaa9f4f350cca3c5754bed2be06..067b96d882e4cc36a0231ee8dc6316f65fc70ad6 100644 (file)
@@ -40,7 +40,7 @@ public class HighlightableBuilder extends PerspectiveBuilder<Highlightable> {
   @Override
   public Highlightable loadPerspective(Class<Highlightable> perspectiveClass, BatchComponent component) {
     if (component.isFile()) {
-      InputFile path = (InputFile) component.inputPath();
+      InputFile path = (InputFile) component.inputComponent();
       return new DefaultHighlightable((DefaultInputFile) path, sensorStorage);
     }
     return null;
index 05ab9db0a457a35dc4ee4200d004de42db3dc0de..fc377e0d8209ec0d3a85560f227dcdf6d551a113 100644 (file)
@@ -41,7 +41,7 @@ public class SymbolizableBuilder extends PerspectiveBuilder<Symbolizable> {
   @Override
   public Symbolizable loadPerspective(Class<Symbolizable> perspectiveClass, BatchComponent component) {
     if (component.isFile()) {
-      InputFile path = (InputFile) component.inputPath();
+      InputFile path = (InputFile) component.inputComponent();
       return new DefaultSymbolizable((DefaultInputFile) path, sensorStorage);
     }
     return null;
index 296bc8713bb8d2047247bbd8b5f13b28737ea342..b6446d45eddc955842e8ec1713381ec301b61bf0 100644 (file)
@@ -40,7 +40,7 @@ public class TestPlanBuilder extends PerspectiveBuilder<MutableTestPlan> {
   @Override
   public MutableTestPlan loadPerspective(Class<MutableTestPlan> perspectiveClass, BatchComponent component) {
     if (component.isFile()) {
-      InputFile inputFile = (InputFile) component.inputPath();
+      InputFile inputFile = (InputFile) component.inputComponent();
       if (inputFile.type() == Type.TEST) {
         if (!testPlanByFile.containsKey(inputFile)) {
           testPlanByFile.put(inputFile, new DefaultTestPlan());
index e1927ecdba6750bfd48e1d625ab10ef9e95c52d2..fa3da4f2220544de0155857c0f350a1b0616b21c 100644 (file)
@@ -37,7 +37,7 @@ public class TestableBuilder extends PerspectiveBuilder<MutableTestable> {
   @Override
   public MutableTestable loadPerspective(Class<MutableTestable> perspectiveClass, BatchComponent component) {
     if (component.isFile()) {
-      InputFile inputFile = (InputFile) component.inputPath();
+      InputFile inputFile = (InputFile) component.inputComponent();
       if (inputFile.type() == Type.MAIN) {
         return new DefaultTestable((DefaultInputFile) inputFile);
       }
index b99aefe9bb857506c255742642024e225d9a7573..e83ce83d15790a20d147052e4936e81825806443 100644 (file)
@@ -21,7 +21,8 @@ package org.sonar.batch.issue;
 
 import org.junit.Test;
 import org.sonar.api.issue.Issue;
-import org.sonar.core.issue.DefaultIssue;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.protocol.output.BatchReport;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
@@ -38,19 +39,19 @@ public class IssueFiltersTest {
     org.sonar.api.issue.IssueFilter ko = mock(org.sonar.api.issue.IssueFilter.class);
     when(ko.accept(any(Issue.class))).thenReturn(false);
 
-    IssueFilters filters = new IssueFilters(new org.sonar.api.issue.IssueFilter[] {ok, ko});
-    assertThat(filters.accept(new DefaultIssue())).isFalse();
+    IssueFilters filters = new IssueFilters(new Project("foo"), new org.sonar.api.issue.IssueFilter[] {ok, ko});
+    assertThat(filters.accept("foo:src/Foo.java", BatchReport.Issue.newBuilder().build())).isFalse();
 
-    filters = new IssueFilters(new org.sonar.api.issue.IssueFilter[] {ok});
-    assertThat(filters.accept(new DefaultIssue())).isTrue();
+    filters = new IssueFilters(new Project("foo"), new org.sonar.api.issue.IssueFilter[] {ok});
+    assertThat(filters.accept("foo:src/Foo.java", BatchReport.Issue.newBuilder().build())).isTrue();
 
-    filters = new IssueFilters(new org.sonar.api.issue.IssueFilter[] {ko});
-    assertThat(filters.accept(new DefaultIssue())).isFalse();
+    filters = new IssueFilters(new Project("foo"), new org.sonar.api.issue.IssueFilter[] {ko});
+    assertThat(filters.accept("foo:src/Foo.java", BatchReport.Issue.newBuilder().build())).isFalse();
   }
 
   @Test
   public void should_always_accept_if_no_filters() {
-    IssueFilters filters = new IssueFilters();
-    assertThat(filters.accept(new DefaultIssue())).isTrue();
+    IssueFilters filters = new IssueFilters(new Project("foo"));
+    assertThat(filters.accept("foo:src/Foo.java", BatchReport.Issue.newBuilder().build())).isTrue();
   }
 }
index fb9cf921e2f4b4c007776feed4ede96355b9333c..0d50150a8a1b4e80af435955fce557f58e5c9b66 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.batch.issue;
 
 import java.io.StringReader;
-import java.util.Date;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -35,7 +34,6 @@ import org.sonar.api.batch.rule.internal.RulesBuilder;
 import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
 import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
 import org.sonar.api.resources.File;
-import org.sonar.api.resources.Project;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.MessageException;
@@ -46,6 +44,7 @@ import org.sonar.batch.report.ReportPublisher;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
@@ -59,8 +58,6 @@ public class ModuleIssuesTest {
   static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle");
   static final String SQUID_RULE_NAME = "Avoid Cycle";
 
-  Project project = new Project("foo").setAnalysisDate(new Date());
-
   @Mock
   IssueFilters filters;
 
@@ -75,14 +72,14 @@ public class ModuleIssuesTest {
 
   @Before
   public void prepare() {
-    componentCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputPath(file);
+    componentCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputComponent(file);
   }
 
   @Test
   public void fail_on_unknown_rule() {
     initModuleIssues();
     DefaultIssue issue = new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
       .forRule(SQUID_RULE_KEY);
     try {
       moduleIssues.initAndAddIssue(issue);
@@ -99,7 +96,7 @@ public class ModuleIssuesTest {
     ruleBuilder.add(SQUID_RULE_KEY).setInternalKey(SQUID_RULE_KEY.rule());
     initModuleIssues();
     DefaultIssue issue = new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message(""))
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
       .forRule(SQUID_RULE_KEY);
     try {
       moduleIssues.initAndAddIssue(issue);
@@ -116,7 +113,7 @@ public class ModuleIssuesTest {
     ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
     initModuleIssues();
     DefaultIssue issue = new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
       .forRule(SQUID_RULE_KEY);
     boolean added = moduleIssues.initAndAddIssue(issue);
 
@@ -131,7 +128,7 @@ public class ModuleIssuesTest {
     initModuleIssues();
 
     DefaultIssue issue = new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
       .forRule(SQUID_RULE_KEY);
     boolean added = moduleIssues.initAndAddIssue(issue);
 
@@ -146,11 +143,11 @@ public class ModuleIssuesTest {
     initModuleIssues();
 
     DefaultIssue issue = new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
       .forRule(SQUID_RULE_KEY)
       .overrideSeverity(org.sonar.api.batch.rule.Severity.CRITICAL);
 
-    when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true);
+    when(filters.accept(anyString(), any(BatchReport.Issue.class))).thenReturn(true);
 
     boolean added = moduleIssues.initAndAddIssue(issue);
 
@@ -167,9 +164,9 @@ public class ModuleIssuesTest {
     initModuleIssues();
 
     DefaultIssue issue = new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
       .forRule(SQUID_RULE_KEY);
-    when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true);
+    when(filters.accept(anyString(), any(BatchReport.Issue.class))).thenReturn(true);
     moduleIssues.initAndAddIssue(issue);
 
     ArgumentCaptor<BatchReport.Issue> argument = ArgumentCaptor.forClass(BatchReport.Issue.class);
@@ -184,9 +181,9 @@ public class ModuleIssuesTest {
     initModuleIssues();
 
     DefaultIssue issue = new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message(""))
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
       .forRule(SQUID_RULE_KEY);
-    when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true);
+    when(filters.accept(anyString(), any(BatchReport.Issue.class))).thenReturn(true);
 
     boolean added = moduleIssues.initAndAddIssue(issue);
 
@@ -203,10 +200,10 @@ public class ModuleIssuesTest {
     initModuleIssues();
 
     DefaultIssue issue = new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message(""))
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
       .forRule(SQUID_RULE_KEY);
 
-    when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(false);
+    when(filters.accept(anyString(), any(BatchReport.Issue.class))).thenReturn(false);
 
     boolean added = moduleIssues.initAndAddIssue(issue);
 
@@ -218,7 +215,7 @@ public class ModuleIssuesTest {
    * Every rules and active rules has to be added in builders before creating ModuleIssues
    */
   private void initModuleIssues() {
-    moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), project, filters, reportPublisher, componentCache);
+    moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), filters, reportPublisher, componentCache);
   }
 
 }
index a9c1317b014e402f06fffa4486531aabd3b98bdd..3411fd118e4a381706db914a0628467f7373d087 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.batch.mediumtest.issues;
 
 import java.io.File;
+import java.util.List;
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
@@ -29,6 +30,8 @@ import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.input.ActiveRule;
 import org.sonar.batch.protocol.input.Rule;
+import org.sonar.batch.protocol.output.BatchReport.Issue;
+import org.sonar.batch.protocol.output.BatchReport.IssueLocation;
 import org.sonar.xoo.XooPlugin;
 import org.sonar.xoo.rule.XooRulesDefinition;
 
@@ -47,9 +50,19 @@ public class MultilineIssuesMediumTest {
     .activateRule(new ActiveRule("xoo", "MultilineIssue", null, "Multinile Issue", "MAJOR", null, "xoo"))
     .build();
 
+  private TaskResult result;
+
   @Before
-  public void prepare() {
+  public void prepare() throws Exception {
     tester.start();
+
+    File projectDir = new File(MultilineIssuesMediumTest.class.getResource("/mediumtest/xoo/sample-multiline").toURI());
+    File tmpDir = temp.newFolder();
+    FileUtils.copyDirectory(projectDir, tmpDir);
+
+    result = tester
+      .newScanTask(new File(tmpDir, "sonar-project.properties"))
+      .start();
   }
 
   @After
@@ -59,16 +72,54 @@ public class MultilineIssuesMediumTest {
 
   @Test
   public void testIssueRange() throws Exception {
-    File projectDir = new File(MultilineIssuesMediumTest.class.getResource("/mediumtest/xoo/sample-multiline").toURI());
-    File tmpDir = temp.newFolder();
-    FileUtils.copyDirectory(projectDir, tmpDir);
+    List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Single.xoo"));
+    assertThat(issues).hasSize(1);
+    Issue issue = issues.get(0);
+    assertThat(issue.getLine()).isEqualTo(6);
+    assertThat(issue.getMsg()).isEqualTo("Primary location");
+    IssueLocation primaryLocation = issue.getPrimaryLocation();
+    assertThat(primaryLocation.getMsg()).isEqualTo("Primary location");
+    assertThat(primaryLocation.getTextRange().getStartLine()).isEqualTo(6);
+    assertThat(primaryLocation.getTextRange().getStartOffset()).isEqualTo(25);
+    assertThat(primaryLocation.getTextRange().getEndLine()).isEqualTo(6);
+    assertThat(primaryLocation.getTextRange().getEndOffset()).isEqualTo(52);
+  }
 
-    TaskResult result = tester
-      .newScanTask(new File(tmpDir, "sonar-project.properties"))
-      .start();
+  @Test
+  public void testMultilineIssueRange() throws Exception {
+    List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Multiline.xoo"));
+    assertThat(issues).hasSize(1);
+    Issue issue = issues.get(0);
+    assertThat(issue.getLine()).isEqualTo(6);
+    assertThat(issue.getMsg()).isEqualTo("Primary location");
+    IssueLocation primaryLocation = issue.getPrimaryLocation();
+    assertThat(primaryLocation.getMsg()).isEqualTo("Primary location");
+    assertThat(primaryLocation.getTextRange().getStartLine()).isEqualTo(6);
+    assertThat(primaryLocation.getTextRange().getStartOffset()).isEqualTo(25);
+    assertThat(primaryLocation.getTextRange().getEndLine()).isEqualTo(7);
+    assertThat(primaryLocation.getTextRange().getEndOffset()).isEqualTo(23);
+  }
 
-    assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(1);
+  @Test
+  public void testMultipleIssueLocation() throws Exception {
+    List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Multiple.xoo"));
+    assertThat(issues).hasSize(1);
+    Issue issue = issues.get(0);
+    assertThat(issue.getLine()).isEqualTo(6);
+    assertThat(issue.getMsg()).isEqualTo("Primary location");
+    IssueLocation primaryLocation = issue.getPrimaryLocation();
+    assertThat(primaryLocation.getMsg()).isEqualTo("Primary location");
+    assertThat(primaryLocation.getTextRange().getStartLine()).isEqualTo(6);
+    assertThat(primaryLocation.getTextRange().getStartOffset()).isEqualTo(25);
+    assertThat(primaryLocation.getTextRange().getEndLine()).isEqualTo(6);
+    assertThat(primaryLocation.getTextRange().getEndOffset()).isEqualTo(52);
 
+    assertThat(issue.getAdditionalLocationList()).hasSize(1);
+    IssueLocation additionalLocation = issue.getAdditionalLocation(0);
+    assertThat(additionalLocation.getMsg()).isEqualTo("Location #2");
+    assertThat(additionalLocation.getTextRange().getStartLine()).isEqualTo(7);
+    assertThat(additionalLocation.getTextRange().getStartOffset()).isEqualTo(25);
+    assertThat(additionalLocation.getTextRange().getEndLine()).isEqualTo(7);
+    assertThat(additionalLocation.getTextRange().getEndOffset()).isEqualTo(52);
   }
-
 }
index 3ba49c04107d3f117e059b8be8e39c10bf6fe79b..11610fa10c18836189589e811c6ec68e8460c364 100644 (file)
@@ -247,8 +247,8 @@ public class PreviewAndReportsMediumTest {
       .setIssueListener(issueListener)
       .start();
 
-    assertThat(result.trackedIssues()).hasSize(14);
-    assertThat(issueListener.issueList).hasSize(14);
+    assertThat(result.trackedIssues()).hasSize(17);
+    assertThat(issueListener.issueList).hasSize(17);
     assertThat(result.trackedIssues()).containsExactlyElementsOf(issueListener.issueList);
   }
 
index 081dcb968b58b52487c7e3f665eebed020eb7a6e..a350ba4a9524a08d79218434b67623aad71b5559 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.batch.postjob;
 
+import java.util.Arrays;
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.batch.AnalysisMode;
@@ -26,12 +27,10 @@ import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.postjob.issue.Issue;
 import org.sonar.api.batch.rule.Severity;
 import org.sonar.api.config.Settings;
-import org.sonar.core.issue.DefaultIssue;
 import org.sonar.api.resources.File;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.issue.IssueCache;
-
-import java.util.Arrays;
+import org.sonar.core.issue.DefaultIssue;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
@@ -77,11 +76,11 @@ public class DefaultPostJobContextTest {
     assertThat(issue.line()).isEqualTo(1);
     assertThat(issue.message()).isEqualTo("msg");
     assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
-    assertThat(issue.inputPath()).isNull();
+    assertThat(issue.inputComponent()).isNull();
 
     InputFile inputPath = mock(InputFile.class);
-    resourceCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputPath(inputPath);
-    assertThat(issue.inputPath()).isEqualTo(inputPath);
+    resourceCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputComponent(inputPath);
+    assertThat(issue.inputComponent()).isEqualTo(inputPath);
 
   }
 }
index d859b83ce47622e0d7085c50174ff6356bf2ecc4..49b7de0dc3c212267b6ba2a9f0b55e039cb6afc9 100644 (file)
@@ -25,7 +25,9 @@ import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.resources.Directory;
 import org.sonar.api.resources.Java;
 import org.sonar.api.resources.Project;
@@ -55,33 +57,33 @@ public class ComponentsPublisherTest {
     Project root = new Project("foo").setName("Root project").setDescription("Root description")
       .setAnalysisDate(DateUtils.parseDate(("2012-12-12")));
     root.setId(1).setUuid("PROJECT_UUID");
-    resourceCache.add(root, null);
+    resourceCache.add(root, null).setInputComponent(new DefaultInputModule("foo"));
 
     Project module1 = new Project("module1").setName("Module1").setDescription("Module description");
     module1.setParent(root);
     module1.setId(2).setUuid("MODULE_UUID");
-    resourceCache.add(module1, root);
+    resourceCache.add(module1, root).setInputComponent(new DefaultInputModule("module1"));
     rootDef.addSubProject(ProjectDefinition.create().setKey("module1"));
 
     Directory dir = Directory.create("src");
     dir.setEffectiveKey("module1:src");
     dir.setId(3).setUuid("DIR_UUID");
-    resourceCache.add(dir, module1);
+    resourceCache.add(dir, module1).setInputComponent(new DefaultInputDir("foo", "src"));
 
     org.sonar.api.resources.File file = org.sonar.api.resources.File.create("src/Foo.java", Java.INSTANCE, false);
     file.setEffectiveKey("module1:src/Foo.java");
     file.setId(4).setUuid("FILE_UUID");
-    resourceCache.add(file, dir).setInputPath(new DefaultInputFile("module1", "src/Foo.java").setLines(2));
+    resourceCache.add(file, dir).setInputComponent(new DefaultInputFile("module1", "src/Foo.java").setLines(2));
 
     org.sonar.api.resources.File fileWithoutLang = org.sonar.api.resources.File.create("src/make", null, false);
     fileWithoutLang.setEffectiveKey("module1:src/make");
     fileWithoutLang.setId(5).setUuid("FILE_WITHOUT_LANG_UUID");
-    resourceCache.add(fileWithoutLang, dir).setInputPath(new DefaultInputFile("module1", "src/make").setLines(10));
+    resourceCache.add(fileWithoutLang, dir).setInputComponent(new DefaultInputFile("module1", "src/make").setLines(10));
 
     org.sonar.api.resources.File testFile = org.sonar.api.resources.File.create("test/FooTest.java", Java.INSTANCE, true);
     testFile.setEffectiveKey("module1:test/FooTest.java");
     testFile.setId(6).setUuid("TEST_FILE_UUID");
-    resourceCache.add(testFile, dir).setInputPath(new DefaultInputFile("module1", "test/FooTest.java").setLines(4));
+    resourceCache.add(testFile, dir).setInputComponent(new DefaultInputFile("module1", "test/FooTest.java").setLines(4));
 
     ImmutableProjectReactor reactor = new ImmutableProjectReactor(rootDef);
 
@@ -122,14 +124,14 @@ public class ComponentsPublisherTest {
     Project root = new Project("foo:my_branch").setName("Root project")
       .setAnalysisDate(DateUtils.parseDate(("2012-12-12")));
     root.setId(1).setUuid("PROJECT_UUID");
-    resourceCache.add(root, null);
+    resourceCache.add(root, null).setInputComponent(new DefaultInputModule("foo"));
     rootDef.properties().put(CoreProperties.LINKS_HOME_PAGE, "http://home");
     rootDef.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "my_branch");
 
     Project module1 = new Project("module1:my_branch").setName("Module1");
     module1.setParent(root);
     module1.setId(2).setUuid("MODULE_UUID");
-    resourceCache.add(module1, root);
+    resourceCache.add(module1, root).setInputComponent(new DefaultInputModule("module1"));
     ProjectDefinition moduleDef = ProjectDefinition.create().setKey("module1");
     moduleDef.properties().put(CoreProperties.LINKS_CI, "http://ci");
     rootDef.addSubProject(moduleDef);
@@ -137,12 +139,12 @@ public class ComponentsPublisherTest {
     Directory dir = Directory.create("src");
     dir.setEffectiveKey("module1:my_branch:my_branch:src");
     dir.setId(3).setUuid("DIR_UUID");
-    resourceCache.add(dir, module1);
+    resourceCache.add(dir, module1).setInputComponent(new DefaultInputDir("foo", "src"));
 
     org.sonar.api.resources.File file = org.sonar.api.resources.File.create("src/Foo.java", Java.INSTANCE, false);
     file.setEffectiveKey("module1:my_branch:my_branch:src/Foo.java");
     file.setId(4).setUuid("FILE_UUID");
-    resourceCache.add(file, dir).setInputPath(new DefaultInputFile("module1", "src/Foo.java").setLines(2));
+    resourceCache.add(file, dir).setInputComponent(new DefaultInputFile("module1", "src/Foo.java").setLines(2));
 
     ImmutableProjectReactor reactor = new ImmutableProjectReactor(rootDef);
 
index e143fd3e3d0de254ed553ab1af0a2c3618e01176..a9f6b81068e3adf03f07fbad113f416e3359de8f 100644 (file)
@@ -28,6 +28,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.resources.Project;
@@ -58,8 +59,8 @@ public class CoveragePublisherTest {
     Project p = new Project("foo").setAnalysisDate(new Date(1234567L));
     BatchComponentCache resourceCache = new BatchComponentCache();
     sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
-    resourceCache.add(p, null);
-    resourceCache.add(sampleFile, null).setInputPath(new DefaultInputFile("foo", "src/Foo.php").setLines(5));
+    resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo"));
+    resourceCache.add(sampleFile, null).setInputComponent(new DefaultInputFile("foo", "src/Foo.php").setLines(5));
     measureCache = mock(MeasureCache.class);
     when(measureCache.byMetric(anyString(), anyString())).thenReturn(Collections.<Measure>emptyList());
     publisher = new CoveragePublisher(resourceCache, measureCache);
index 8fe1eff1f1767f7d671ee5ad8295c732507c8b29..731bad05db4cfabec75477c26e787ef729748dab 100644 (file)
  */
 package org.sonar.batch.report;
 
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.sensor.duplication.Duplication;
 import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication;
 import org.sonar.api.resources.Project;
@@ -31,11 +36,6 @@ import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.protocol.output.BatchReport;
 import org.sonar.batch.protocol.output.BatchReportReader;
 import org.sonar.batch.protocol.output.BatchReportWriter;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
 import org.sonar.core.util.CloseableIterator;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -55,11 +55,11 @@ public class DuplicationsPublisherTest {
   public void prepare() {
     BatchComponentCache resourceCache = new BatchComponentCache();
     Project p = new Project("foo");
-    resourceCache.add(p, null);
+    resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo"));
     org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
-    resourceCache.add(sampleFile, null);
+    resourceCache.add(sampleFile, null).setInputComponent(new DefaultInputFile("foo", "src/Foo.php").setLines(5));
     org.sonar.api.resources.Resource sampleFile2 = org.sonar.api.resources.File.create("src/Foo2.php").setEffectiveKey("foo:src/Foo2.php");
-    resourceCache.add(sampleFile2, null);
+    resourceCache.add(sampleFile2, null).setInputComponent(new DefaultInputFile("foo", "src/Foo2.php").setLines(5));
     duplicationCache = mock(DuplicationCache.class);
     when(duplicationCache.byComponent(anyString())).thenReturn(Collections.<DefaultDuplication>emptyList());
     publisher = new DuplicationsPublisher(resourceCache, duplicationCache);
index 83f58a78afeb81ac4ecd9de66f197a90dcc5415c..ae21b89551edf4291c96f72a67084218b5a08e40 100644 (file)
@@ -29,6 +29,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.batch.index.BatchComponentCache;
@@ -55,10 +56,10 @@ public class SourcePublisherTest {
     BatchComponentCache resourceCache = new BatchComponentCache();
     sampleFile = org.sonar.api.resources.File.create("src/Foo.php");
     sampleFile.setEffectiveKey("foo:src/Foo.php");
-    resourceCache.add(p, null);
+    resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo"));
     File baseDir = temp.newFolder();
     sourceFile = new File(baseDir, "src/Foo.php");
-    resourceCache.add(sampleFile, null).setInputPath(
+    resourceCache.add(sampleFile, null).setInputComponent(
       new DefaultInputFile("foo", "src/Foo.php").setLines(5).setModuleBaseDir(baseDir.toPath()).setCharset(StandardCharsets.ISO_8859_1));
     publisher = new SourcePublisher(resourceCache);
     File outputDir = temp.newFolder();
index 5d8a9a5cdf31090af7aeab032a30a42e9b2977e2..3ad99b57f3151d0e8456699fdf250fb6a203862e 100644 (file)
@@ -106,7 +106,7 @@ public class DefaultSensorStorageTest {
 
     ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class);
     Resource sonarFile = File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
-    resourceCache.add(sonarFile, null).setInputPath(file);
+    resourceCache.add(sonarFile, null).setInputComponent(file);
     when(measureCache.put(eq(sonarFile), argumentCaptor.capture())).thenReturn(null);
     sensorStorage.store(new DefaultMeasure()
       .onFile(file)
index 3d05b7eb3ed5055f8e38c059397f64fded882e4a..2b3d0cc4abf451864159ce944ff54ad26d55820d 100644 (file)
@@ -20,6 +20,8 @@
 package org.sonar.batch.source;
 
 import org.junit.Test;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.sensor.internal.SensorStorage;
 import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
@@ -35,7 +37,7 @@ public class HighlightableBuilderTest {
   @Test
   public void should_load_default_perspective() {
     Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c");
-    BatchComponent component = new BatchComponent(1, file, null);
+    BatchComponent component = new BatchComponent(1, file, null).setInputComponent(new DefaultInputFile("foo", "foo.c"));
 
     HighlightableBuilder builder = new HighlightableBuilder(mock(SensorStorage.class));
     Highlightable perspective = builder.loadPerspective(Highlightable.class, component);
@@ -45,7 +47,7 @@ public class HighlightableBuilderTest {
 
   @Test
   public void project_should_not_be_highlightable() {
-    BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null);
+    BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null).setInputComponent(new DefaultInputModule("struts"));
 
     HighlightableBuilder builder = new HighlightableBuilder(mock(SensorStorage.class));
     Highlightable perspective = builder.loadPerspective(Highlightable.class, component);
index 9b29c6ff3c32147c23cffe2ae83f7519035b112e..2bc3f32368f702f2c7669ce6ace3896102817cbd 100644 (file)
@@ -21,6 +21,8 @@
 package org.sonar.batch.source;
 
 import org.junit.Test;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.component.Perspective;
 import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
@@ -37,7 +39,7 @@ public class SymbolizableBuilderTest {
   @Test
   public void should_load_perspective() {
     Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c");
-    BatchComponent component = new BatchComponent(1, file, null);
+    BatchComponent component = new BatchComponent(1, file, null).setInputComponent(new DefaultInputFile("foo", "foo.c"));
 
     SymbolizableBuilder perspectiveBuilder = new SymbolizableBuilder(mock(DefaultSensorStorage.class));
     Perspective perspective = perspectiveBuilder.loadPerspective(Symbolizable.class, component);
@@ -47,7 +49,7 @@ public class SymbolizableBuilderTest {
 
   @Test
   public void project_should_not_be_highlightable() {
-    BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null);
+    BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null).setInputComponent(new DefaultInputModule("struts"));
 
     SymbolizableBuilder builder = new SymbolizableBuilder(mock(DefaultSensorStorage.class));
     Perspective perspective = builder.loadPerspective(Symbolizable.class, component);
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/HelloJava.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/HelloJava.xoo
deleted file mode 100644 (file)
index 0b815e0..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-package hello;
-
-public class HelloJava {
-
-  public static void main(String[] args) {
-    {xoo-start-issue:1:1}System.out.println("Hello"){xoo-end-issue:1:1};
-  }
-}
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/HelloJava.xoo.measures b/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/HelloJava.xoo.measures
deleted file mode 100644 (file)
index 9eaf8ba..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-ncloc:3
-complexity:1
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/Multiline.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/Multiline.xoo
new file mode 100644 (file)
index 0000000..4043133
--- /dev/null
@@ -0,0 +1,9 @@
+package hello;
+
+public class HelloJava {
+
+  public static void main(String[] args) {
+    {xoo-start-issue:1:1}System.out
+      .println("Hello"){xoo-end-issue:1:1};
+  }
+}
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/Multiple.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/Multiple.xoo
new file mode 100644 (file)
index 0000000..c3840bf
--- /dev/null
@@ -0,0 +1,9 @@
+package hello;
+
+public class HelloJava {
+
+  public static void main(String[] args) {
+    {xoo-start-issue:1:1}System.out.println("Hello"){xoo-end-issue:1:1};
+    {xoo-start-issue:1:2}System.out.println("World"){xoo-end-issue:1:2};
+  }
+}
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/Single.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/Single.xoo
new file mode 100644 (file)
index 0000000..0b815e0
--- /dev/null
@@ -0,0 +1,8 @@
+package hello;
+
+public class HelloJava {
+
+  public static void main(String[] args) {
+    {xoo-start-issue:1:1}System.out.println("Hello"){xoo-end-issue:1:1};
+  }
+}
\ No newline at end of file
index caa6f3d86630423b70d659c41e3905326da30aeb..4a107e459489e7fd1bc61d18a30fc4cc73d87f6c 100644 (file)
@@ -85,6 +85,11 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder {
     throw unsupported();
   }
 
+  @Override
+  public IssueBuilder at(NewIssueLocation location) {
+    throw unsupported();
+  }
+
   @Override
   public IssueBuilder addLocation(NewIssueLocation location) {
     throw unsupported();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputComponent.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputComponent.java
new file mode 100644 (file)
index 0000000..e4f66be
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs;
+
+/**
+ * Common interface for all input components.
+ *
+ * @since 5.2
+ * @see InputFile
+ * @see InputDir
+ */
+public interface InputComponent {
+
+  /**
+   * Is the component an {@link InputFile}
+   */
+  boolean isFile();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java
new file mode 100644 (file)
index 0000000..a92dc36
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs;
+
+/**
+ * Used to create issues and measures on modules.
+ *
+ * @since 5.2
+ */
+public interface InputModule extends InputComponent {
+
+}
index 121df157028d0e6a89e092a989f6a9a02a9de133..b63d955a4c254ed3a8ddcc622a5ec833bac08fbc 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.api.batch.fs;
 
 import java.io.File;
-import java.io.Serializable;
 import java.nio.file.Path;
 
 /**
@@ -30,7 +29,7 @@ import java.nio.file.Path;
  * @see InputFile
  * @see InputDir
  */
-public interface InputPath extends Serializable {
+public interface InputPath extends InputComponent {
 
   /**
    * @see InputFile#relativePath()
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputComponent.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputComponent.java
new file mode 100644 (file)
index 0000000..9a49dd4
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import org.sonar.api.batch.fs.InputComponent;
+
+/**
+ * @since 5.2
+ */
+public abstract class DefaultInputComponent implements InputComponent {
+
+  public abstract String key();
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || this.getClass() != o.getClass()) {
+      return false;
+    }
+
+    DefaultInputComponent that = (DefaultInputComponent) o;
+    return key().equals(that.key());
+  }
+
+  @Override
+  public int hashCode() {
+    return key().hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return "[key=" + key() + "]";
+  }
+}
index 8979e1f3468646c9969639ca4edc78deac6af73e..5e1ebb651c238756846443e0c9dba3e30dde5d65 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
-import org.sonar.api.batch.fs.InputDir;
-import org.sonar.api.utils.PathUtils;
-
 import java.io.File;
 import java.nio.file.Path;
+import org.sonar.api.batch.fs.InputDir;
+import org.sonar.api.utils.PathUtils;
 
 /**
  * @since 4.5
  */
-public class DefaultInputDir implements InputDir {
+public class DefaultInputDir extends DefaultInputComponent implements InputDir {
 
   private final String relativePath;
   private final String moduleKey;
@@ -66,6 +65,7 @@ public class DefaultInputDir implements InputDir {
     return moduleKey;
   }
 
+  @Override
   public String key() {
     return new StringBuilder().append(moduleKey).append(":").append(relativePath).toString();
   }
@@ -78,6 +78,11 @@ public class DefaultInputDir implements InputDir {
     return this;
   }
 
+  @Override
+  public boolean isFile() {
+    return false;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) {
index 3c0cb4a243b409afbcc791cff67b1dae3c623036..a2e8e992472e8380fbbc6cc2652c53fab520b763 100644 (file)
@@ -39,7 +39,7 @@ import org.sonar.api.utils.PathUtils;
 /**
  * @since 4.2
  */
-public class DefaultInputFile implements InputFile, org.sonar.api.resources.InputFile {
+public class DefaultInputFile extends DefaultInputComponent implements InputFile, org.sonar.api.resources.InputFile {
 
   private final String relativePath;
   private final String moduleKey;
@@ -114,6 +114,7 @@ public class DefaultInputFile implements InputFile, org.sonar.api.resources.Inpu
   /**
    * Component key.
    */
+  @Override
   public String key() {
     return new StringBuilder().append(moduleKey).append(":").append(relativePath).toString();
   }
@@ -327,4 +328,9 @@ public class DefaultInputFile implements InputFile, org.sonar.api.resources.Inpu
     return new BufferedInputStream(new FileInputStream(file()));
   }
 
+  @Override
+  public boolean isFile() {
+    return true;
+  }
+
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java
new file mode 100644 (file)
index 0000000..e1dd688
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import org.sonar.api.batch.fs.InputModule;
+
+/**
+ * @since 5.2
+ */
+public class DefaultInputModule extends DefaultInputComponent implements InputModule {
+
+  private final String moduleKey;
+
+  public DefaultInputModule(String moduleKey) {
+    this.moduleKey = moduleKey;
+  }
+
+  @Override
+  public String key() {
+    return moduleKey;
+  }
+
+  @Override
+  public boolean isFile() {
+    return false;
+  }
+
+}
index ccf79d800fb3e50db1151df48f03cd633ea9653f..6027568a0cff63db1a671d58ed1117abd9784b5e 100644 (file)
 package org.sonar.api.batch.postjob.issue;
 
 import com.google.common.annotations.Beta;
-import org.sonar.api.batch.fs.InputPath;
+import javax.annotation.CheckForNull;
+import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.batch.rule.Severity;
 import org.sonar.api.rule.RuleKey;
 
-import javax.annotation.CheckForNull;
-
 /**
  * Represents an issue state at the end of the batch analysis. Only available after local issue tracking in preview mode.
  *
@@ -50,10 +49,10 @@ public interface Issue {
   String componentKey();
 
   /**
-   * The {@link InputPath} this issue belongs to. Returns null if issue is global to the project or if component was deleted (for resolved issues).
+   * The {@link InputComponent} this issue belongs to. Returns null if component was deleted (for resolved issues).
    */
   @CheckForNull
-  InputPath inputPath();
+  InputComponent inputComponent();
 
   /**
    * Line of the issue. Null for global issues and issues on directories. Can also be null
index 1c519115e297798bbbd16d499d1a78ae30cbd054..563c848ce0f93fc801ce56406491a54d82f09372 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.api.batch.sensor.issue;
 
 import com.google.common.annotations.Beta;
 import java.util.List;
+import java.util.Map;
 import javax.annotation.CheckForNull;
 import org.sonar.api.batch.rule.Severity;
 import org.sonar.api.batch.sensor.Sensor;
@@ -59,7 +60,13 @@ public interface Issue {
   Severity overriddenSeverity();
 
   /**
-   * List of locations for this issue. Returns at least one location.
+   * Primary locations for this issue.
+   * @since 5.2
+   */
+  IssueLocation primaryLocation();
+
+  /**
+   * List of additional locations for this issue.
    * @since 5.2
    */
   List<IssueLocation> locations();
@@ -70,4 +77,10 @@ public interface Issue {
    */
   List<ExecutionFlow> executionFlows();
 
+  /**
+   * Key/value pair of attributes that are attached to the issue.
+   * @since 5.2
+   */
+  Map<String, String> attributes();
+
 }
index fa587f89cb8c71d4e7fa07da8e7cc1a7188eca63..aff5cd8947d6fef398106612a1fa37821361e187 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.api.batch.sensor.issue;
 
 import com.google.common.annotations.Beta;
 import javax.annotation.CheckForNull;
-import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.batch.fs.TextRange;
 
 /**
@@ -33,10 +33,9 @@ import org.sonar.api.batch.fs.TextRange;
 public interface IssueLocation {
 
   /**
-   * The {@link InputPath} this location belongs to. Returns null if location is global to the project.
+   * The {@link InputComponent} this location belongs to.
    */
-  @CheckForNull
-  InputPath inputPath();
+  InputComponent inputComponent();
 
   /**
    * Range of the issue. Null for global issues and issues on directories. Can also be null
index 7c44f331163f1b8a7faed4bbd3d016f9c965539c..8ad694545b3e0642eaa5546756a2b9e2908e5318 100644 (file)
@@ -51,7 +51,13 @@ public interface NewIssue {
 
   /**
    * @since 5.2
-   * Register a new location for this issue. First registered location is considered as primary location.
+   * Primary for this issue.
+   */
+  NewIssue at(NewIssueLocation primaryLocation);
+
+  /**
+   * @since 5.2
+   * Register an additional location for this issue.
    */
   NewIssue addLocation(NewIssueLocation location);
 
@@ -67,6 +73,12 @@ public interface NewIssue {
    */
   NewIssueLocation newLocation();
 
+  /**
+   * @since 5.2
+   * Attach a new attribute to the issue. Not used by SQ but can be reused later for integration needs (for example it is returned by WS).
+   */
+  NewIssue addAttribute(String key, String value);
+
   /**
    * Save the issue. If rule key is unknown or rule not enabled in the current quality profile then a warning is logged but no exception
    * is thrown.
index 3ead59ed7f5a6019d7bb98b30bc146f28cd5def1..e0f4281a65a94b81eee7822c368a5263d8bd45c7 100644 (file)
@@ -20,7 +20,7 @@
 package org.sonar.api.batch.sensor.issue;
 
 import com.google.common.annotations.Beta;
-import org.sonar.api.batch.fs.InputDir;
+import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.TextRange;
 
@@ -38,22 +38,12 @@ public interface NewIssueLocation {
   int MESSAGE_MAX_SIZE = 4000;
 
   /**
-   * The {@link InputFile} the issue location belongs to. For global issues call {@link #onProject()}.
+   * The {@link InputComponent} the issue location belongs to. Mandatory.
    */
-  NewIssueLocation onFile(InputFile file);
+  NewIssueLocation on(InputComponent component);
 
   /**
-   * The {@link InputDir} the issue location belongs to. For global issues call {@link #onProject()}.
-   */
-  NewIssueLocation onDir(InputDir inputDir);
-
-  /**
-   * Tell that the issue location is global to the project.
-   */
-  NewIssueLocation onProject();
-
-  /**
-   * Position in the file. Only valid when {@link #onFile(InputFile)} has been called. 
+   * Position in the file. Only applicable when {@link #on(InputComponent)} has been called with an InputFile. 
    * See {@link InputFile#newRange(org.sonar.api.batch.fs.TextPointer, org.sonar.api.batch.fs.TextPointer)}
    */
   NewIssueLocation at(TextRange location);
index 686677ffb29692bad268e5a4a02a92575994091d..2018f7ac0ed7a0552f29270912a409b5f2831ee0 100644 (file)
@@ -21,11 +21,13 @@ package org.sonar.api.batch.sensor.issue.internal;
 
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import javax.annotation.Nullable;
 import org.sonar.api.batch.rule.Severity;
 import org.sonar.api.batch.sensor.internal.DefaultStorable;
@@ -35,25 +37,23 @@ import org.sonar.api.batch.sensor.issue.IssueLocation;
 import org.sonar.api.batch.sensor.issue.NewIssue;
 import org.sonar.api.batch.sensor.issue.NewIssueLocation;
 import org.sonar.api.rule.RuleKey;
-import org.sonar.api.utils.internal.Uuids;
 
 public class DefaultIssue extends DefaultStorable implements Issue, NewIssue {
 
-  private String key;
   private RuleKey ruleKey;
   private Double effortToFix;
   private Severity overriddenSeverity;
+  private IssueLocation primaryLocation;
   private List<IssueLocation> locations = new ArrayList<>();
   private List<List<IssueLocation>> executionFlows = new ArrayList<>();
+  private final Map<String, String> attributes = new LinkedHashMap<>();
 
   public DefaultIssue() {
     super(null);
-    this.key = Uuids.create();
   }
 
   public DefaultIssue(SensorStorage storage) {
     super(storage);
-    this.key = Uuids.create();
   }
 
   @Override
@@ -79,6 +79,14 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue {
     return new DefaultIssueLocation();
   }
 
+  @Override
+  public DefaultIssue at(NewIssueLocation primaryLocation) {
+    Preconditions.checkArgument(primaryLocation != null, "Cannot use a location that is null");
+    Preconditions.checkState(this.primaryLocation == null, "at() already called");
+    this.primaryLocation = (DefaultIssueLocation) primaryLocation;
+    return this;
+  }
+
   @Override
   public DefaultIssue addLocation(NewIssueLocation location) {
     locations.add((DefaultIssueLocation) location);
@@ -95,6 +103,17 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue {
     return null;
   }
 
+  @Override
+  public DefaultIssue addAttribute(String key, String value) {
+    attributes.put(key, value);
+    return this;
+  }
+
+  @Override
+  public Map<String, String> attributes() {
+    return ImmutableMap.copyOf(attributes);
+  }
+
   @Override
   public RuleKey ruleKey() {
     return this.ruleKey;
@@ -110,8 +129,9 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue {
     return this.effortToFix;
   }
 
-  public String key() {
-    return this.key;
+  @Override
+  public IssueLocation primaryLocation() {
+    return primaryLocation;
   }
 
   @Override
@@ -137,34 +157,8 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue {
   @Override
   public void doSave() {
     Preconditions.checkNotNull(this.ruleKey, "ruleKey is mandatory on issue");
-    Preconditions.checkState(!Strings.isNullOrEmpty(key), "Fail to generate issue key");
-    Preconditions.checkState(!locations.isEmpty(), "At least one location is mandatory on every issue");
+    Preconditions.checkState(primaryLocation != null, "Primary location is mandatory on every issue");
     storage.store(this);
   }
 
-  /**
-   * For testing only.
-   */
-  public DefaultIssue withKey(String key) {
-    this.key = key;
-    return this;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
-    }
-    DefaultIssue that = (DefaultIssue) o;
-    return !key.equals(that.key);
-  }
-
-  @Override
-  public int hashCode() {
-    return key.hashCode();
-  }
-
 }
index 3840cd595f820867ca4b91a9b7bb9427b345e52f..87b6ff67d448798dab466a2a606e176adb19dc2e 100644 (file)
 package org.sonar.api.batch.sensor.issue.internal;
 
 import com.google.common.base.Preconditions;
-import javax.annotation.CheckForNull;
-import org.sonar.api.batch.fs.InputDir;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.batch.fs.TextRange;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.issue.IssueLocation;
@@ -31,46 +28,23 @@ import org.sonar.api.batch.sensor.issue.NewIssueLocation;
 
 public class DefaultIssueLocation implements NewIssueLocation, IssueLocation {
 
-  private static final String INPUT_DIR_SHOULD_BE_NON_NULL = "InputDir should be non null";
-  private static final String INPUT_FILE_SHOULD_BE_NON_NULL = "InputFile should be non null";
-  private static final String ON_FILE_OR_ON_DIR_ALREADY_CALLED = "onFile or onDir already called";
-  private static final String ON_PROJECT_ALREADY_CALLED = "onProject already called";
-
-  private boolean onProject = false;
-  private InputPath path;
+  private InputComponent component;
   private TextRange textRange;
   private String message;
 
   @Override
-  public NewIssueLocation onFile(InputFile file) {
-    Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED);
-    Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED);
-    Preconditions.checkNotNull(file, INPUT_FILE_SHOULD_BE_NON_NULL);
-    this.path = file;
-    return this;
-  }
-
-  @Override
-  public NewIssueLocation onDir(InputDir dir) {
-    Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED);
-    Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED);
-    Preconditions.checkNotNull(dir, INPUT_DIR_SHOULD_BE_NON_NULL);
-    this.path = dir;
-    return this;
-  }
-
-  @Override
-  public NewIssueLocation onProject() {
-    Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED);
-    Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED);
-    this.onProject = true;
+  public NewIssueLocation on(InputComponent component) {
+    Preconditions.checkArgument(component != null, "Component can't be null");
+    Preconditions.checkState(this.component == null, "on() already called");
+    this.component = component;
     return this;
   }
 
   @Override
   public NewIssueLocation at(TextRange location) {
-    Preconditions.checkState(this.path != null && this.path instanceof InputFile, "at() should be called after onFile.");
-    DefaultInputFile file = (DefaultInputFile) this.path;
+    Preconditions.checkState(this.component != null, "at() should be called after on()");
+    Preconditions.checkState(this.component.isFile(), "at() should be called only for an InputFile.");
+    DefaultInputFile file = (DefaultInputFile) this.component;
     file.validate(location);
     this.textRange = location;
     return this;
@@ -86,9 +60,8 @@ public class DefaultIssueLocation implements NewIssueLocation, IssueLocation {
   }
 
   @Override
-  @CheckForNull
-  public InputPath inputPath() {
-    return this.path;
+  public InputComponent inputComponent() {
+    return this.component;
   }
 
   @Override
index 7cf03cb057938753d0f718ae45ace29e8ba40343..c2adf0e5e11934c24508b90c105c1f8b0f4ef0fb 100644 (file)
@@ -67,7 +67,7 @@ public interface Issuable extends Perspective {
 
     /**
      * Optional line index, starting from 1. It must not be zero or negative.
-     * @deprecated since 5.2 use {@link #addLocation(NewIssueLocation)}
+     * @deprecated since 5.2 use {@link #at(NewIssueLocation)}
      */
     @Deprecated
     IssueBuilder line(@Nullable Integer line);
@@ -76,7 +76,7 @@ public interface Issuable extends Perspective {
      * Optional, but recommended, plain-text message.
      * <p/>
      * Formats like Markdown or HTML are not supported. Size must not be greater than {@link Issue#MESSAGE_MAX_SIZE} characters.
-     * @deprecated since 5.2 use {@link #addLocation(NewIssueLocation)}
+     * @deprecated since 5.2 use {@link #at(NewIssueLocation)}
      */
     @Deprecated
     IssueBuilder message(@Nullable String message);
@@ -87,6 +87,12 @@ public interface Issuable extends Perspective {
      */
     NewIssueLocation newLocation();
 
+    /**
+     * @since 5.2
+     * Register primary location for this issue.
+     */
+    IssueBuilder at(NewIssueLocation primaryLocation);
+
     /**
      * @since 5.2
      * Register a new secondary location for this issue.
index 8837c6eb6da373694ee27893fe01da3d0de63175..7c15bd2c3f96b26967867781eaa120bd3d684a4e 100644 (file)
@@ -89,12 +89,12 @@ public class SensorContextTesterTest {
     assertThat(tester.allIssues()).isEmpty();
     NewIssue newIssue = tester.newIssue();
     newIssue
-      .addLocation(newIssue.newLocation().onFile(new DefaultInputFile("foo", "src/Foo.java")))
+      .at(newIssue.newLocation().on(new DefaultInputFile("foo", "src/Foo.java")))
       .forRule(RuleKey.of("repo", "rule"))
       .save();
     newIssue = tester.newIssue();
     newIssue
-      .addLocation(newIssue.newLocation().onFile(new DefaultInputFile("foo", "src/Foo.java")))
+      .at(newIssue.newLocation().on(new DefaultInputFile("foo", "src/Foo.java")))
       .forRule(RuleKey.of("repo", "rule"))
       .save();
     assertThat(tester.allIssues()).hasSize(2);
index 6ed1a7aa686ed2d10cfed2e0c329b06d154b7140..cffb6367bafd1fe0245aa16ce57e20af035855f2 100644 (file)
@@ -35,19 +35,19 @@ public class DefaultIssueLocationTest {
   private DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php").initMetadata(new FileMetadata().readMetadata(new StringReader("Foo\nBar\n")));
 
   @Test
-  public void not_allowed_to_call_onFile_and_onProject() {
+  public void not_allowed_to_call_on_twice() {
     thrown.expect(IllegalStateException.class);
-    thrown.expectMessage("onProject already called");
+    thrown.expectMessage("on() already called");
     new DefaultIssueLocation()
-      .onProject()
-      .onFile(inputFile)
+      .on(inputFile)
+      .on(inputFile)
       .message("Wrong way!");
   }
 
   @Test
   public void prevent_too_long_messages() {
     new DefaultIssueLocation()
-      .onFile(inputFile)
+      .on(inputFile)
       .message(StringUtils.repeat("a", 4000));
 
     thrown.expect(IllegalArgumentException.class);
@@ -55,7 +55,7 @@ public class DefaultIssueLocationTest {
     thrown.expectMessage("aaa] size is 4001");
 
     new DefaultIssueLocation()
-      .onFile(inputFile)
+      .on(inputFile)
       .message(StringUtils.repeat("a", 4001));
 
   }
index e9b27548b359626d2e710bc2f1b643e87fe60959..011683854e178febade48a6029b5ca5bb4c55d55 100644 (file)
@@ -23,6 +23,7 @@ import java.io.StringReader;
 import org.junit.Test;
 import org.sonar.api.batch.fs.internal.DefaultInputDir;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.batch.rule.Severity;
 import org.sonar.api.batch.sensor.internal.SensorStorage;
@@ -40,18 +41,18 @@ public class DefaultIssueTest {
   public void build_file_issue() {
     SensorStorage storage = mock(SensorStorage.class);
     DefaultIssue issue = new DefaultIssue(storage)
-      .addLocation(new DefaultIssueLocation()
-        .onFile(inputFile)
+      .at(new DefaultIssueLocation()
+        .on(inputFile)
         .at(inputFile.selectLine(1))
         .message("Wrong way!"))
       .forRule(RuleKey.of("repo", "rule"))
       .effortToFix(10.0);
 
-    assertThat(issue.locations().get(0).inputPath()).isEqualTo(inputFile);
+    assertThat(issue.primaryLocation().inputComponent()).isEqualTo(inputFile);
     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
-    assertThat(issue.locations().get(0).textRange().start().line()).isEqualTo(1);
+    assertThat(issue.primaryLocation().textRange().start().line()).isEqualTo(1);
     assertThat(issue.effortToFix()).isEqualTo(10.0);
-    assertThat(issue.locations().get(0).message()).isEqualTo("Wrong way!");
+    assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!");
 
     issue.save();
 
@@ -62,16 +63,16 @@ public class DefaultIssueTest {
   public void build_directory_issue() {
     SensorStorage storage = mock(SensorStorage.class);
     DefaultIssue issue = new DefaultIssue(storage)
-      .addLocation(new DefaultIssueLocation()
-        .onDir(new DefaultInputDir("foo", "src"))
+      .at(new DefaultIssueLocation()
+        .on(new DefaultInputDir("foo", "src"))
         .message("Wrong way!"))
       .forRule(RuleKey.of("repo", "rule"))
       .overrideSeverity(Severity.BLOCKER);
 
-    assertThat(issue.locations().get(0).inputPath()).isEqualTo(new DefaultInputDir("foo", "src"));
+    assertThat(issue.primaryLocation().inputComponent()).isEqualTo(new DefaultInputDir("foo", "src"));
     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
-    assertThat(issue.locations().get(0).textRange()).isNull();
-    assertThat(issue.locations().get(0).message()).isEqualTo("Wrong way!");
+    assertThat(issue.primaryLocation().textRange()).isNull();
+    assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!");
     assertThat(issue.overriddenSeverity()).isEqualTo(Severity.BLOCKER);
 
     issue.save();
@@ -83,17 +84,17 @@ public class DefaultIssueTest {
   public void build_project_issue() {
     SensorStorage storage = mock(SensorStorage.class);
     DefaultIssue issue = new DefaultIssue(storage)
-      .addLocation(new DefaultIssueLocation()
-        .onProject()
+      .at(new DefaultIssueLocation()
+        .on(new DefaultInputModule("foo"))
         .message("Wrong way!"))
       .forRule(RuleKey.of("repo", "rule"))
       .effortToFix(10.0);
 
-    assertThat(issue.locations().get(0).inputPath()).isNull();
+    assertThat(issue.primaryLocation().inputComponent()).isEqualTo(new DefaultInputModule("foo"));
     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
-    assertThat(issue.locations().get(0).textRange()).isNull();
+    assertThat(issue.primaryLocation().textRange()).isNull();
     assertThat(issue.effortToFix()).isEqualTo(10.0);
-    assertThat(issue.locations().get(0).message()).isEqualTo("Wrong way!");
+    assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!");
 
     issue.save();