]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6319 SONAR-6321 Feed highlighting and symbols in compute report 186/head
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 30 Mar 2015 07:29:50 +0000 (09:29 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 31 Mar 2015 19:42:42 +0000 (21:42 +0200)
104 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/AbstractNewCoverageFileAnalyzer.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzer.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewItCoverageFileAnalyzer.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewOverallCoverageFileAnalyzer.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzerTest.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SyntaxHighlightingSensorTest.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
sonar-batch/src/main/java/org/sonar/batch/deprecated/InputFileComponent.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/deprecated/perspectives/BatchPerspectives.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingData.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataValueCoder.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRuleValueCoder.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/highlighting/package-info.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java
sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/index/SourceDataFactory.java
sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java
sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java
sonar-batch/src/main/java/org/sonar/batch/issue/tracking/FileHashes.java
sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
sonar-batch/src/main/java/org/sonar/batch/phases/DatabaseLessPhaseExecutor.java
sonar-batch/src/main/java/org/sonar/batch/phases/DatabaseModePhaseExecutor.java
sonar-batch/src/main/java/org/sonar/batch/report/BatchReportUtils.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java
sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java
sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java
sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java
sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java
sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisherStep.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileMetadata.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileMetadata.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java
sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java
sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizerSensor.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizers.java
sonar-batch/src/main/java/org/sonar/batch/source/DefaultHighlightable.java
sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbol.java
sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java
sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java
sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java
sonar-batch/src/main/java/org/sonar/batch/source/HighlightingCodeBuilder.java
sonar-batch/src/main/java/org/sonar/batch/source/HighlightingRenderer.java
sonar-batch/src/main/java/org/sonar/batch/source/LinesSensor.java
sonar-batch/src/main/java/org/sonar/batch/source/SymbolizableBuilder.java
sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/symbol/package-info.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilderTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/index/SourceDataFactoryTest.java
sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingDecoratorTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/report/PublishReportJobTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileMetadataTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java
sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
sonar-batch/src/test/java/org/sonar/batch/source/CodeColorizersTest.java
sonar-batch/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java
sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java
sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.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/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java [deleted file]
sonar-batch/src/test/resources/org/sonar/batch/scan/filesystem/glyphicons-halflings-regular.woff [deleted file]
sonar-core/src/main/java/org/sonar/core/component/GraphPerspectiveBuilder.java
sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java
sonar-core/src/main/java/org/sonar/core/component/ScanPerspectives.java [deleted file]
sonar-core/src/test/java/org/sonar/core/component/PerspectiveBuilderTest.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextPointer.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextRange.java [new file with mode: 0644]
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/DefaultTextPointer.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextRange.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/internal/DefaultHighlighting.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/internal/SyntaxHighlightingRule.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java
sonar-plugin-api/src/main/java/org/sonar/api/component/Perspectives.java
sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java
sonar-plugin-api/src/main/java/org/sonar/api/source/Symbol.java
sonar-plugin-api/src/main/java/org/sonar/api/source/Symbolizable.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FileMetadataTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/highlighting/internal/DefaultHighlightingTest.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java
sonar-plugin-api/src/test/resources/org/sonar/api/batch/fs/internal/glyphicons-halflings-regular.woff [new file with mode: 0644]

index 1ac4c1a4f3a264c82a9cb535dffb641f82d18028..c6ecc1ba9001c876c024bdc0c81bf1f890018ff2 100644 (file)
@@ -36,7 +36,7 @@ import org.sonar.batch.index.ResourceCache;
 import org.sonar.batch.protocol.output.BatchReport.Scm;
 import org.sonar.batch.protocol.output.BatchReport.Scm.Changeset;
 import org.sonar.batch.protocol.output.BatchReportReader;
-import org.sonar.batch.report.PublishReportJob;
+import org.sonar.batch.report.ReportPublisher;
 
 import javax.annotation.Nullable;
 
@@ -53,17 +53,17 @@ import java.util.Map;
 public abstract class AbstractNewCoverageFileAnalyzer implements Decorator {
 
   private final List<PeriodStruct> structs;
-  private final PublishReportJob publishReportJob;
+  private final ReportPublisher publishReportJob;
   private final ResourceCache resourceCache;
 
-  public AbstractNewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, PublishReportJob publishReportJob, ResourceCache resourceCache) {
+  public AbstractNewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, ResourceCache resourceCache) {
     this(Lists.<PeriodStruct>newArrayList(), publishReportJob, resourceCache);
     for (Period period : timeMachineConfiguration.periods()) {
       structs.add(new PeriodStruct(period.getIndex(), period.getDate()));
     }
   }
 
-  AbstractNewCoverageFileAnalyzer(List<PeriodStruct> structs, PublishReportJob publishReportJob, ResourceCache resourceCache) {
+  AbstractNewCoverageFileAnalyzer(List<PeriodStruct> structs, ReportPublisher publishReportJob, ResourceCache resourceCache) {
     this.resourceCache = resourceCache;
     this.publishReportJob = publishReportJob;
     this.structs = structs;
index 38aac00f0eaf78180752dff44f48bee387a521df..d13df7224ef753b1fdf9b09ba943029461e631a8 100644 (file)
@@ -23,17 +23,17 @@ import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Metric;
 import org.sonar.batch.components.TimeMachineConfiguration;
 import org.sonar.batch.index.ResourceCache;
-import org.sonar.batch.report.PublishReportJob;
+import org.sonar.batch.report.ReportPublisher;
 
 import java.util.List;
 
 public class NewCoverageFileAnalyzer extends AbstractNewCoverageFileAnalyzer {
 
-  public NewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, PublishReportJob publishReportJob, ResourceCache resourceCache) {
+  public NewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, ResourceCache resourceCache) {
     super(timeMachineConfiguration, publishReportJob, resourceCache);
   }
 
-  NewCoverageFileAnalyzer(List<PeriodStruct> structs, PublishReportJob publishReportJob, ResourceCache resourceCache) {
+  NewCoverageFileAnalyzer(List<PeriodStruct> structs, ReportPublisher publishReportJob, ResourceCache resourceCache) {
     super(structs, publishReportJob, resourceCache);
   }
 
index 48222f59ce2ab6b87de708df79f4b1ac01c43273..f069a633b4ed5cd6636423de6b211ac6310ca57d 100644 (file)
@@ -23,11 +23,11 @@ import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Metric;
 import org.sonar.batch.components.TimeMachineConfiguration;
 import org.sonar.batch.index.ResourceCache;
-import org.sonar.batch.report.PublishReportJob;
+import org.sonar.batch.report.ReportPublisher;
 
 public class NewItCoverageFileAnalyzer extends AbstractNewCoverageFileAnalyzer {
 
-  public NewItCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, PublishReportJob publishReportJob, ResourceCache resourceCache) {
+  public NewItCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, ResourceCache resourceCache) {
     super(timeMachineConfiguration, publishReportJob, resourceCache);
   }
 
index 75a1a83686c7d7611f4156efd64a312b6a75e0f6..fc61e4eafe3a6632ada2e02188537d5d238ce959 100644 (file)
@@ -23,11 +23,11 @@ import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Metric;
 import org.sonar.batch.components.TimeMachineConfiguration;
 import org.sonar.batch.index.ResourceCache;
-import org.sonar.batch.report.PublishReportJob;
+import org.sonar.batch.report.ReportPublisher;
 
 public class NewOverallCoverageFileAnalyzer extends AbstractNewCoverageFileAnalyzer {
 
-  public NewOverallCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, PublishReportJob publishReportJob, ResourceCache resourceCache) {
+  public NewOverallCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, ResourceCache resourceCache) {
     super(timeMachineConfiguration, publishReportJob, resourceCache);
   }
 
index af6d25828a6a1e091a845f9c1232885ce8202496..6f02ac2f6264fa52a9007a6dd99483b69dd0898b 100644 (file)
@@ -35,7 +35,7 @@ import org.sonar.batch.index.ResourceCache;
 import org.sonar.batch.protocol.output.BatchReport.Scm;
 import org.sonar.batch.protocol.output.BatchReport.Scm.Changeset;
 import org.sonar.batch.protocol.output.BatchReportWriter;
-import org.sonar.batch.report.PublishReportJob;
+import org.sonar.batch.report.ReportPublisher;
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -69,7 +69,7 @@ public class NewCoverageFileAnalyzerTest {
     List<AbstractNewCoverageFileAnalyzer.PeriodStruct> structs = Arrays.asList(
       new AbstractNewCoverageFileAnalyzer.PeriodStruct(1, newDate("2009-12-25")),
       new AbstractNewCoverageFileAnalyzer.PeriodStruct(3, newDate("2011-02-18")));
-    PublishReportJob publishReportJob = mock(PublishReportJob.class);
+    ReportPublisher publishReportJob = mock(ReportPublisher.class);
     java.io.File reportBaseDir = temp.newFolder();
     when(publishReportJob.getReportDir()).thenReturn(reportBaseDir);
     writer = new BatchReportWriter(reportBaseDir);
index d31724c73e8f52f8262ed85f7a27085ffa9751a9..9d27f526e3354aee1540469913b3e14eed703141 100644 (file)
@@ -25,12 +25,14 @@ 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.FileMetadata;
 import org.sonar.api.batch.sensor.highlighting.TypeOfText;
 import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
 import org.sonar.api.batch.sensor.internal.SensorContextTester;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.StringReader;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -66,12 +68,13 @@ public class SyntaxHighlightingSensorTest {
   public void testExecution() throws IOException {
     File symbol = new File(baseDir, "src/foo.xoo.highlighting");
     FileUtils.write(symbol, "1:4:k\n12:15:cppd\n\n#comment");
-    DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo").setLastValidOffset(100);
+    DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo")
+      .initMetadata(new FileMetadata().readMetadata(new StringReader(" xoo\nazertyazer\nfoo")));
     context.fileSystem().add(inputFile);
 
     sensor.execute(context);
 
-    assertThat(context.highlightingTypeFor("foo:src/foo.xoo", 2)).containsOnly(TypeOfText.KEYWORD);
-    assertThat(context.highlightingTypeFor("foo:src/foo.xoo", 13)).containsOnly(TypeOfText.CPP_DOC);
+    assertThat(context.highlightingTypeAt("foo:src/foo.xoo", 1, 2)).containsOnly(TypeOfText.KEYWORD);
+    assertThat(context.highlightingTypeAt("foo:src/foo.xoo", 2, 8)).containsOnly(TypeOfText.CPP_DOC);
   }
 }
index 0358d1428f6f37b2ba66fb9dc58ee1bd4ba847c5..6a19dc49ab7899d4a4833dce60bf494759081514 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.batch.maven.MavenProjectConverter;
 import org.sonar.batch.scan.report.*;
 import org.sonar.batch.scm.ScmConfiguration;
 import org.sonar.batch.scm.ScmSensor;
+import org.sonar.batch.source.CodeColorizerSensor;
 import org.sonar.batch.source.LinesSensor;
 import org.sonar.core.computation.dbcleaner.DefaultPurgeTask;
 import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner;
@@ -59,6 +60,7 @@ public class BatchComponents {
       ScmSensor.class,
 
       LinesSensor.class,
+      CodeColorizerSensor.class,
 
       // Issues tracking
       IssueTracking.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/deprecated/InputFileComponent.java b/sonar-batch/src/main/java/org/sonar/batch/deprecated/InputFileComponent.java
new file mode 100644 (file)
index 0000000..c79b9af
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.batch.deprecated;
+
+import org.sonar.api.batch.fs.InputFile.Type;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.component.Component;
+import org.sonar.api.resources.Qualifiers;
+
+public class InputFileComponent implements Component {
+
+  private final DefaultInputFile inputFile;
+
+  public InputFileComponent(DefaultInputFile inputFile) {
+    this.inputFile = inputFile;
+  }
+
+  @Override
+  public String key() {
+    return inputFile.key();
+  }
+
+  @Override
+  public String path() {
+    return inputFile.relativePath();
+  }
+
+  @Override
+  public String name() {
+    return inputFile.file().getName();
+  }
+
+  @Override
+  public String longName() {
+    return inputFile.relativePath();
+  }
+
+  @Override
+  public String qualifier() {
+    return inputFile.type() == Type.MAIN ? Qualifiers.FILE : Qualifiers.UNIT_TEST_FILE;
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/deprecated/perspectives/BatchPerspectives.java b/sonar-batch/src/main/java/org/sonar/batch/deprecated/perspectives/BatchPerspectives.java
new file mode 100644 (file)
index 0000000..d743cca
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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.batch.deprecated.perspectives;
+
+import org.sonar.core.component.PerspectiveBuilder;
+import org.sonar.core.component.PerspectiveNotFoundException;
+import org.sonar.core.component.ResourceComponent;
+
+import com.google.common.collect.Maps;
+import org.sonar.api.batch.SonarIndex;
+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.component.Component;
+import org.sonar.api.component.Perspective;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Resource;
+
+import javax.annotation.CheckForNull;
+
+import java.util.Map;
+
+public class BatchPerspectives implements ResourcePerspectives {
+
+  private final Map<Class<?>, PerspectiveBuilder<?>> builders = Maps.newHashMap();
+  private final SonarIndex resourceIndex;
+
+  public BatchPerspectives(PerspectiveBuilder[] builders, SonarIndex resourceIndex) {
+    this.resourceIndex = resourceIndex;
+    for (PerspectiveBuilder builder : builders) {
+      // TODO check duplications
+      this.builders.put(builder.getPerspectiveClass(), builder);
+    }
+  }
+
+  @Override
+  @CheckForNull
+  public <P extends Perspective> P as(Class<P> perspectiveClass, Component component) {
+    if (component.key() == null) {
+      return null;
+    }
+    PerspectiveBuilder<P> builder = builderFor(perspectiveClass);
+    return builder.loadPerspective(perspectiveClass, component);
+  }
+
+  @Override
+  @CheckForNull
+  public <P extends Perspective> P as(Class<P> perspectiveClass, Resource resource) {
+    Resource indexedResource = resource;
+    if (resource.getEffectiveKey() == null) {
+      indexedResource = resourceIndex.getResource(resource);
+    }
+    if (indexedResource != null) {
+      return as(perspectiveClass, new ResourceComponent(indexedResource));
+    }
+    return null;
+  }
+
+  @Override
+  public <P extends Perspective> P as(Class<P> perspectiveClass, InputPath inputPath) {
+    Resource r;
+    if (inputPath instanceof InputDir) {
+      r = Directory.create(((InputDir) inputPath).relativePath());
+    } else if (inputPath instanceof InputFile) {
+      r = File.create(((InputFile) inputPath).relativePath());
+    } else {
+      throw new IllegalArgumentException("Unknow input path type: " + inputPath);
+    }
+    return as(perspectiveClass, r);
+  }
+
+  private <T extends Perspective> PerspectiveBuilder<T> builderFor(Class<T> clazz) {
+    PerspectiveBuilder<T> builder = (PerspectiveBuilder<T>) builders.get(clazz);
+    if (builder == null) {
+      throw new PerspectiveNotFoundException("Perspective class is not registered: " + clazz);
+    }
+    return builder;
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingData.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingData.java
deleted file mode 100644 (file)
index 7b9b334..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.batch.highlighting;
-
-import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
-import org.sonar.batch.index.Data;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-public class SyntaxHighlightingData implements Data {
-
-  public static final String FIELD_SEPARATOR = ",";
-  public static final String RULE_SEPARATOR = ";";
-
-  private List<SyntaxHighlightingRule> syntaxHighlightingRuleSet;
-
-  public SyntaxHighlightingData(Collection<SyntaxHighlightingRule> syntaxHighlightingRuleSet) {
-    this.syntaxHighlightingRuleSet = new ArrayList<SyntaxHighlightingRule>(syntaxHighlightingRuleSet);
-  }
-
-  public List<SyntaxHighlightingRule> syntaxHighlightingRuleSet() {
-    return syntaxHighlightingRuleSet;
-  }
-
-  @Override
-  public String writeString() {
-    StringBuilder sb = new StringBuilder();
-    for (SyntaxHighlightingRule highlightingRule : syntaxHighlightingRuleSet) {
-      if (sb.length() > 0) {
-        sb.append(RULE_SEPARATOR);
-      }
-      sb.append(highlightingRule.getStartPosition())
-        .append(FIELD_SEPARATOR)
-        .append(highlightingRule.getEndPosition())
-        .append(FIELD_SEPARATOR)
-        .append(highlightingRule.getTextType().cssClass());
-    }
-
-    return sb.toString();
-  }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java
deleted file mode 100644 (file)
index be0fa73..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.batch.highlighting;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
-import org.sonar.api.batch.sensor.highlighting.TypeOfText;
-import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
-
-import javax.annotation.Nullable;
-
-import java.util.Iterator;
-import java.util.Set;
-
-public class SyntaxHighlightingDataBuilder {
-
-  private Set<SyntaxHighlightingRule> syntaxHighlightingRuleSet;
-
-  public SyntaxHighlightingDataBuilder() {
-    syntaxHighlightingRuleSet = Sets.newTreeSet(new Ordering<SyntaxHighlightingRule>() {
-      @Override
-      public int compare(@Nullable SyntaxHighlightingRule left,
-        @Nullable SyntaxHighlightingRule right) {
-        int result = left.getStartPosition() - right.getStartPosition();
-        if (result == 0) {
-          result = right.getEndPosition() - left.getEndPosition();
-        }
-        return result;
-      }
-    });
-  }
-
-  @VisibleForTesting
-  public Set<SyntaxHighlightingRule> getSyntaxHighlightingRuleSet() {
-    return syntaxHighlightingRuleSet;
-  }
-
-  public SyntaxHighlightingDataBuilder registerHighlightingRule(int startOffset, int endOffset, TypeOfText typeOfText) {
-    SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(startOffset, endOffset,
-      typeOfText);
-    this.syntaxHighlightingRuleSet.add(syntaxHighlightingRule);
-    return this;
-  }
-
-  public SyntaxHighlightingData build() {
-    checkOverlappingBoudaries();
-    return new SyntaxHighlightingData(syntaxHighlightingRuleSet);
-  }
-
-  private void checkOverlappingBoudaries() {
-    if (syntaxHighlightingRuleSet.size() > 1) {
-      Iterator<SyntaxHighlightingRule> it = syntaxHighlightingRuleSet.iterator();
-      SyntaxHighlightingRule previous = it.next();
-      while (it.hasNext()) {
-        SyntaxHighlightingRule current = it.next();
-        if (previous.getEndPosition() > current.getStartPosition() && !(previous.getEndPosition() >= current.getEndPosition())) {
-          String errorMsg = String.format("Cannot register highlighting rule for characters from %s to %s as it " +
-            "overlaps at least one existing rule", current.getStartPosition(), current.getEndPosition());
-          throw new IllegalStateException(errorMsg);
-        }
-        previous = current;
-      }
-    }
-  }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataValueCoder.java
deleted file mode 100644 (file)
index a748b49..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.batch.highlighting;
-
-import com.persistit.Value;
-import com.persistit.encoding.CoderContext;
-import com.persistit.encoding.ValueCoder;
-import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SyntaxHighlightingDataValueCoder implements ValueCoder {
-
-  private SyntaxHighlightingRuleValueCoder rulesCoder = new SyntaxHighlightingRuleValueCoder();
-
-  @Override
-  public void put(Value value, Object object, CoderContext context) {
-    SyntaxHighlightingData data = (SyntaxHighlightingData) object;
-    value.put(data.syntaxHighlightingRuleSet().size());
-    for (SyntaxHighlightingRule rule : data.syntaxHighlightingRuleSet()) {
-      rulesCoder.put(value, rule, context);
-    }
-  }
-
-  @Override
-  public Object get(Value value, Class clazz, CoderContext context) {
-    int count = value.getInt();
-    List<SyntaxHighlightingRule> rules = new ArrayList<SyntaxHighlightingRule>(count);
-    for (int i = 0; i < count; i++) {
-      rules.add((SyntaxHighlightingRule) rulesCoder.get(value, SyntaxHighlightingRule.class, context));
-    }
-    return new SyntaxHighlightingData(rules);
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRuleValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRuleValueCoder.java
deleted file mode 100644 (file)
index 8700c81..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.batch.highlighting;
-
-import com.persistit.Value;
-import com.persistit.encoding.CoderContext;
-import com.persistit.encoding.ValueCoder;
-import org.sonar.api.batch.sensor.highlighting.TypeOfText;
-import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
-
-class SyntaxHighlightingRuleValueCoder implements ValueCoder {
-
-  @Override
-  public void put(Value value, Object object, CoderContext context) {
-    SyntaxHighlightingRule rule = (SyntaxHighlightingRule) object;
-    value.put(rule.getStartPosition());
-    value.put(rule.getEndPosition());
-    value.put(rule.getTextType().ordinal());
-  }
-
-  @Override
-  public Object get(Value value, Class clazz, CoderContext context) {
-    int startPosition = value.getInt();
-    int endPosition = value.getInt();
-    TypeOfText type = TypeOfText.values()[value.getInt()];
-    return SyntaxHighlightingRule.create(startPosition, endPosition, type);
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/package-info.java
deleted file mode 100644 (file)
index 93b92f3..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.batch.highlighting;
-
-import javax.annotation.ParametersAreNonnullByDefault;
index a07eded789c9bb694c1460faf49ebcf84a0e2272..ff8af997c2f55629a60745b3a5c41c47e21f5908 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.batch.index;
 
+import org.sonar.api.batch.fs.InputPath;
 import org.sonar.api.database.model.Snapshot;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.ResourceUtils;
@@ -36,6 +37,7 @@ public class BatchResource {
   private Snapshot s;
   private final BatchResource parent;
   private final Collection<BatchResource> children = new ArrayList<BatchResource>();
+  private InputPath inputPath;
 
   public BatchResource(int batchId, Resource r, @Nullable BatchResource parent) {
     this.batchId = batchId;
@@ -91,4 +93,14 @@ public class BatchResource {
   public boolean isDir() {
     return ResourceUtils.isDirectory(r);
   }
+
+  public BatchResource setInputPath(InputPath inputPath) {
+    this.inputPath = inputPath;
+    return this;
+  }
+
+  @CheckForNull
+  public InputPath inputPath() {
+    return inputPath;
+  }
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java b/sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java
deleted file mode 100644 (file)
index b94aa09..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.batch.index;
-
-import org.sonar.api.BatchComponent;
-import org.sonar.batch.highlighting.SyntaxHighlightingData;
-import org.sonar.batch.highlighting.SyntaxHighlightingDataValueCoder;
-
-import javax.annotation.CheckForNull;
-
-public class ComponentDataCache implements BatchComponent {
-  private final Cache cache;
-
-  public ComponentDataCache(Caches caches) {
-    caches.registerValueCoder(SyntaxHighlightingData.class, new SyntaxHighlightingDataValueCoder());
-    cache = caches.createCache("componentData");
-  }
-
-  public <D extends Data> ComponentDataCache setData(String componentKey, String dataType, D data) {
-    cache.put(componentKey, dataType, data);
-    return this;
-  }
-
-  public ComponentDataCache setStringData(String componentKey, String dataType, String data) {
-    return setData(componentKey, dataType, new StringData(data));
-  }
-
-  @CheckForNull
-  public <D extends Data> D getData(String componentKey, String dataType) {
-    return (D) cache.get(componentKey, dataType);
-  }
-
-  @CheckForNull
-  public String getStringData(String componentKey, String dataType) {
-    Data data = (Data) cache.get(componentKey, dataType);
-    return data == null ? null : ((StringData) data).data();
-  }
-
-  public <D extends Data> Iterable<Cache.Entry<D>> entries(String componentKey) {
-    return cache.entries(componentKey);
-  }
-}
index d2197170309156af6cf45bac74e49f5848ea11bb..8a0184be3e37844649cafccfe2aee8c5ce94e86d 100644 (file)
@@ -26,22 +26,19 @@ import org.sonar.api.BatchComponent;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.duplication.Duplication;
 import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication;
-import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
-import org.sonar.api.source.Symbol;
 import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.batch.duplication.DuplicationCache;
-import org.sonar.batch.highlighting.SyntaxHighlightingData;
+import org.sonar.batch.protocol.output.BatchReport.Range;
 import org.sonar.batch.protocol.output.BatchReport.Scm;
 import org.sonar.batch.protocol.output.BatchReport.Scm.Changeset;
-import org.sonar.batch.protocol.output.BatchReportReader;
-import org.sonar.batch.report.PublishReportJob;
-import org.sonar.batch.scan.filesystem.InputFileMetadata;
+import org.sonar.batch.protocol.output.BatchReport.Symbols;
+import org.sonar.batch.protocol.output.BatchReport.SyntaxHighlighting.HighlightingRule;
+import org.sonar.batch.protocol.output.*;
+import org.sonar.batch.report.BatchReportUtils;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.scan.measure.MeasureCache;
-import org.sonar.batch.source.CodeColorizers;
-import org.sonar.batch.symbol.SymbolData;
-import org.sonar.core.source.SnapshotDataTypes;
 import org.sonar.core.source.db.FileSourceDto;
 import org.sonar.server.source.db.FileSourceDb;
 import org.sonar.server.source.db.FileSourceDb.Data.Builder;
@@ -58,30 +55,25 @@ public class SourceDataFactory implements BatchComponent {
   private static final String BOM = "\uFEFF";
 
   private final MeasureCache measureCache;
-  private final ComponentDataCache componentDataCache;
   private final DuplicationCache duplicationCache;
-  private final CodeColorizers codeColorizers;
-  private final PublishReportJob publishReportJob;
+  private final ReportPublisher reportPublisher;
 
   private final ResourceCache resourceCache;
 
-  public SourceDataFactory(MeasureCache measureCache, ComponentDataCache componentDataCache,
-    DuplicationCache duplicationCache, CodeColorizers codeColorizers, PublishReportJob publishReportJob, ResourceCache resourceCache) {
+  public SourceDataFactory(MeasureCache measureCache, DuplicationCache duplicationCache, ReportPublisher reportPublisher, ResourceCache resourceCache) {
     this.measureCache = measureCache;
-    this.componentDataCache = componentDataCache;
     this.duplicationCache = duplicationCache;
-    this.codeColorizers = codeColorizers;
-    this.publishReportJob = publishReportJob;
+    this.reportPublisher = reportPublisher;
     this.resourceCache = resourceCache;
   }
 
-  public byte[] consolidateData(DefaultInputFile inputFile, InputFileMetadata metadata) throws IOException {
+  public byte[] consolidateData(DefaultInputFile inputFile) throws IOException {
     FileSourceDb.Data.Builder dataBuilder = createForSource(inputFile);
     applyLineMeasures(inputFile, dataBuilder);
     applyScm(inputFile, dataBuilder);
     applyDuplications(inputFile.key(), dataBuilder);
-    applyHighlighting(inputFile, metadata, dataBuilder);
-    applySymbolReferences(inputFile, metadata, dataBuilder);
+    applyHighlighting(inputFile, dataBuilder);
+    applySymbolReferences(inputFile, dataBuilder);
     return FileSourceDto.encodeData(dataBuilder.build());
   }
 
@@ -101,7 +93,7 @@ public class SourceDataFactory implements BatchComponent {
   }
 
   void applyScm(DefaultInputFile inputFile, Builder dataBuilder) {
-    BatchReportReader reader = new BatchReportReader(publishReportJob.getReportDir());
+    BatchReportReader reader = new BatchReportReader(reportPublisher.getReportDir());
     Scm componentScm = reader.readComponentScm(resourceCache.get(inputFile).batchId());
     if (componentScm != null) {
       for (int i = 0; i < componentScm.getChangesetIndexByLineCount(); i++) {
@@ -202,25 +194,22 @@ public class SourceDataFactory implements BatchComponent {
     void apply(String value, FileSourceDb.Line.Builder lineBuilder);
   }
 
-  void applyHighlighting(DefaultInputFile inputFile, InputFileMetadata metadata, FileSourceDb.Data.Builder to) {
-    SyntaxHighlightingData highlighting = componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING);
-    String language = inputFile.language();
-    if (highlighting == null && language != null) {
-      highlighting = codeColorizers.toSyntaxHighlighting(inputFile.file(), inputFile.charset(), language);
-    }
-    if (highlighting == null) {
+  void applyHighlighting(DefaultInputFile inputFile, FileSourceDb.Data.Builder to) {
+    BatchReportReader reader = new BatchReportReader(reportPublisher.getReportDir());
+    List<HighlightingRule> highlightingRules = reader.readComponentSyntaxHighlighting(resourceCache.get(inputFile).batchId());
+    if (highlightingRules.isEmpty()) {
       return;
     }
     StringBuilder[] highlightingPerLine = new StringBuilder[inputFile.lines()];
     RuleItemWriter ruleItemWriter = new RuleItemWriter();
     int currentLineIdx = 1;
-    for (SyntaxHighlightingRule rule : highlighting.syntaxHighlightingRuleSet()) {
-      while (currentLineIdx < inputFile.lines() && rule.getStartPosition() >= metadata.originalLineOffsets()[currentLineIdx]) {
+    for (HighlightingRule rule : highlightingRules) {
+      while (currentLineIdx < inputFile.lines() && rule.getRange().getStartLine() > currentLineIdx) {
         // This rule starts on another line so advance
         currentLineIdx++;
       }
       // Now we know current rule starts on current line
-      writeDataPerLine(metadata.originalLineOffsets(), rule, rule.getStartPosition(), rule.getEndPosition(), highlightingPerLine, currentLineIdx, ruleItemWriter);
+      writeDataPerLine(inputFile.originalLineOffsets(), rule, rule.getRange(), highlightingPerLine, ruleItemWriter);
     }
     for (int i = 0; i < highlightingPerLine.length; i++) {
       StringBuilder sb = highlightingPerLine[i];
@@ -230,76 +219,60 @@ public class SourceDataFactory implements BatchComponent {
     }
   }
 
-  void applySymbolReferences(DefaultInputFile file, InputFileMetadata metadata, FileSourceDb.Data.Builder to) {
-    SymbolData symbolRefs = componentDataCache.getData(file.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING);
-    if (symbolRefs != null) {
-      StringBuilder[] refsPerLine = new StringBuilder[file.lines()];
-      int symbolId = 1;
-      List<Symbol> symbols = new ArrayList<Symbol>(symbolRefs.referencesBySymbol().keySet());
-      // Sort symbols to avoid false variation that would lead to an unnecessary update
-      Collections.sort(symbols, new Comparator<Symbol>() {
-        @Override
-        public int compare(Symbol o1, Symbol o2) {
-          return o1.getDeclarationStartOffset() - o2.getDeclarationStartOffset();
-        }
-      });
-      for (Symbol symbol : symbols) {
-        int declarationStartOffset = symbol.getDeclarationStartOffset();
-        int declarationEndOffset = symbol.getDeclarationEndOffset();
-        int length = declarationEndOffset - declarationStartOffset;
-        addSymbol(symbolId, declarationStartOffset, declarationEndOffset, metadata.originalLineOffsets(), refsPerLine);
-        for (Integer referenceStartOffset : symbolRefs.referencesBySymbol().get(symbol)) {
-          if (referenceStartOffset == declarationStartOffset) {
-            // Ignore old API that used to store reference as first declaration
-            continue;
-          }
-          addSymbol(symbolId, referenceStartOffset, referenceStartOffset + length, metadata.originalLineOffsets(), refsPerLine);
+  void applySymbolReferences(DefaultInputFile inputFile, FileSourceDb.Data.Builder to) {
+    BatchReportReader reader = new BatchReportReader(reportPublisher.getReportDir());
+    List<Symbols.Symbol> symbols = new ArrayList<Symbols.Symbol>(reader.readComponentSymbols(resourceCache.get(inputFile).batchId()));
+    if (symbols.isEmpty()) {
+      return;
+    }
+    StringBuilder[] refsPerLine = new StringBuilder[inputFile.lines()];
+    int symbolId = 1;
+    // Sort symbols to avoid false variation that would lead to an unnecessary update
+    Collections.sort(symbols, new Comparator<Symbols.Symbol>() {
+      @Override
+      public int compare(Symbols.Symbol o1, Symbols.Symbol o2) {
+        if (o1.getDeclaration().getStartLine() == o2.getDeclaration().getStartLine()) {
+          return Integer.compare(o1.getDeclaration().getStartOffset(), o2.getDeclaration().getStartOffset());
+        } else {
+          return Integer.compare(o1.getDeclaration().getStartLine(), o2.getDeclaration().getStartLine());
         }
-        symbolId++;
       }
-      for (int i = 0; i < refsPerLine.length; i++) {
-        StringBuilder sb = refsPerLine[i];
-        if (sb != null) {
-          to.getLinesBuilder(i).setSymbols(sb.toString());
-        }
+    });
+    for (Symbols.Symbol symbol : symbols) {
+      addSymbol(symbolId, symbol.getDeclaration(), inputFile.originalLineOffsets(), refsPerLine);
+      for (Range reference : symbol.getReferenceList()) {
+        addSymbol(symbolId, reference, inputFile.originalLineOffsets(), refsPerLine);
       }
+      symbolId++;
     }
-  }
-
-  private void addSymbol(int symbolId, int startOffset, int endOffset, int[] originalLineOffsets, StringBuilder[] result) {
-    int startLine = binarySearchLine(startOffset, originalLineOffsets);
-    writeDataPerLine(originalLineOffsets, symbolId, startOffset, endOffset, result, startLine, new SymbolItemWriter());
-  }
-
-  private int binarySearchLine(int declarationStartOffset, int[] originalLineOffsets) {
-    int begin = 0;
-    int end = originalLineOffsets.length - 1;
-    while (begin < end) {
-      int mid = (int) Math.round((begin + end) / 2D);
-      if (declarationStartOffset < originalLineOffsets[mid]) {
-        end = mid - 1;
-      } else {
-        begin = mid;
+    for (int i = 0; i < refsPerLine.length; i++) {
+      StringBuilder sb = refsPerLine[i];
+      if (sb != null) {
+        to.getLinesBuilder(i).setSymbols(sb.toString());
       }
     }
-    return begin + 1;
   }
 
-  private <G> void writeDataPerLine(int[] originalLineOffsets, G item, int globalStartOffset, int globalEndOffset, StringBuilder[] dataPerLine, int startLine,
-    RangeItemWriter<G> writer) {
-    int currentLineIdx = startLine;
-    // We know current item starts on current line
-    long ruleStartOffsetCurrentLine = globalStartOffset;
-    while (currentLineIdx < originalLineOffsets.length && globalEndOffset >= originalLineOffsets[currentLineIdx]) {
+  private void addSymbol(int symbolId, Range range, int[] originalLineOffsets, StringBuilder[] result) {
+    writeDataPerLine(originalLineOffsets, symbolId, range, result, new SymbolItemWriter());
+  }
+
+  private <G> void writeDataPerLine(int[] originalLineOffsets, G item, Range range, StringBuilder[] dataPerLine, RangeItemWriter<G> writer) {
+    int currentLineIdx = range.getStartLine();
+    long ruleStartOffsetCurrentLine = range.getStartOffset();
+    while (currentLineIdx < dataPerLine.length && range.getEndLine() > currentLineIdx) {
       // item continue on next line so write current line and continue on next line with same item
-      writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine - originalLineOffsets[currentLineIdx - 1], originalLineOffsets[currentLineIdx]
-        - originalLineOffsets[currentLineIdx - 1], writer);
+      writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine, lineLength(originalLineOffsets, currentLineIdx), writer);
       currentLineIdx++;
-      ruleStartOffsetCurrentLine = originalLineOffsets[currentLineIdx - 1];
+      ruleStartOffsetCurrentLine = 0;
     }
     // item ends on current line
-    writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine - originalLineOffsets[currentLineIdx - 1], globalEndOffset
-      - originalLineOffsets[currentLineIdx - 1], writer);
+    writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine, range.getEndOffset(), writer);
+  }
+
+  private int lineLength(int[] originalLineOffsets, int currentLineIdx) {
+    return originalLineOffsets[currentLineIdx]
+      - originalLineOffsets[currentLineIdx - 1];
   }
 
   private <G> void writeItem(G item, StringBuilder[] dataPerLine, int currentLineIdx, long startLineOffset, long endLineOffset, RangeItemWriter<G> writer) {
@@ -321,17 +294,17 @@ public class SourceDataFactory implements BatchComponent {
     void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, G item);
   }
 
-  private static class RuleItemWriter implements RangeItemWriter<SyntaxHighlightingRule> {
+  private static class RuleItemWriter implements RangeItemWriter<HighlightingRule> {
     @Override
-    public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, SyntaxHighlightingRule item) {
+    public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, HighlightingRule item) {
       if (currentLineSb.length() > 0) {
-        currentLineSb.append(SyntaxHighlightingData.RULE_SEPARATOR);
+        currentLineSb.append(';');
       }
       currentLineSb.append(startLineOffset)
-        .append(SyntaxHighlightingData.FIELD_SEPARATOR)
+        .append(',')
         .append(endLineOffset)
-        .append(SyntaxHighlightingData.FIELD_SEPARATOR)
-        .append(item.getTextType().cssClass());
+        .append(',')
+        .append(BatchReportUtils.toCssClass(item.getType()));
     }
 
   }
@@ -340,12 +313,12 @@ public class SourceDataFactory implements BatchComponent {
     @Override
     public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, Integer symbolId) {
       if (currentLineSb.length() > 0) {
-        currentLineSb.append(SymbolData.SYMBOL_SEPARATOR);
+        currentLineSb.append(";");
       }
       currentLineSb.append(startLineOffset)
-        .append(SymbolData.FIELD_SEPARATOR)
+        .append(",")
         .append(endLineOffset)
-        .append(SymbolData.FIELD_SEPARATOR)
+        .append(",")
         .append(symbolId);
     }
   }
index bf537550a8ca3dae4b4ada61a93beeffc855a6d6..a55562c88f2f5a1414c5172292e63384fe123daf 100644 (file)
@@ -19,6 +19,9 @@
  */
 package org.sonar.batch.index;
 
+import org.sonar.api.batch.fs.internal.FileMetadata;
+import org.sonar.api.batch.fs.internal.FileMetadata.LineHashConsumer;
+
 import org.apache.commons.codec.binary.Hex;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.ibatis.session.ResultContext;
@@ -27,9 +30,6 @@ import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.utils.System2;
 import org.sonar.batch.ProjectTree;
-import org.sonar.batch.scan.filesystem.FileMetadata;
-import org.sonar.batch.scan.filesystem.FileMetadata.LineHashConsumer;
-import org.sonar.batch.scan.filesystem.InputFileMetadata;
 import org.sonar.batch.scan.filesystem.InputPathCache;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
@@ -89,8 +89,7 @@ public class SourcePersister implements ScanPersister {
   private void persist(DbSession session, FileSourceMapper mapper, DefaultInputFile inputFile, Map<String, FileSourceDto> previousDtosByUuid) {
     String fileUuid = resourceCache.get(inputFile).resource().getUuid();
 
-    InputFileMetadata metadata = inputPathCache.getFileMetadata(inputFile);
-    byte[] data = computeData(inputFile, metadata);
+    byte[] data = computeData(inputFile);
     String dataHash = DigestUtils.md5Hex(data);
     FileSourceDto previousDto = previousDtosByUuid.get(fileUuid);
     if (previousDto == null) {
@@ -99,7 +98,7 @@ public class SourcePersister implements ScanPersister {
         .setFileUuid(fileUuid)
         .setBinaryData(data)
         .setDataHash(dataHash)
-        .setSrcHash(metadata.hash())
+        .setSrcHash(inputFile.hash())
         .setLineHashes(lineHashesAsMd5Hex(inputFile))
         .setCreatedAt(system2.now())
         .setUpdatedAt(0L);
@@ -108,12 +107,12 @@ public class SourcePersister implements ScanPersister {
     } else {
       // Update only if data_hash has changed or if src_hash is missing (progressive migration)
       boolean binaryDataUpdated = !dataHash.equals(previousDto.getDataHash());
-      boolean srcHashUpdated = !metadata.hash().equals(previousDto.getSrcHash());
+      boolean srcHashUpdated = !inputFile.hash().equals(previousDto.getSrcHash());
       if (binaryDataUpdated || srcHashUpdated) {
         previousDto
           .setBinaryData(data)
           .setDataHash(dataHash)
-          .setSrcHash(metadata.hash())
+          .setSrcHash(inputFile.hash())
           .setLineHashes(lineHashesAsMd5Hex(inputFile));
         // Optimization only change updated at when updating binary data to avoid unecessary indexation by E/S
         if (binaryDataUpdated) {
@@ -147,9 +146,9 @@ public class SourcePersister implements ScanPersister {
     return result.toString();
   }
 
-  private byte[] computeData(DefaultInputFile inputFile, InputFileMetadata metadata) {
+  private byte[] computeData(DefaultInputFile inputFile) {
     try {
-      return dataFactory.consolidateData(inputFile, metadata);
+      return dataFactory.consolidateData(inputFile);
     } catch (IOException e) {
       throw new IllegalStateException("Fail to read file " + inputFile, e);
     }
index 7491bbb78144e523232b3ae4923da67cf8ad08b4..d287364b046311125ca783b6fdfe0c018e45c11f 100644 (file)
@@ -47,7 +47,7 @@ public class IssuableFactory extends PerspectiveBuilder<Issuable> {
 
   @CheckForNull
   @Override
-  protected Issuable loadPerspective(Class<Issuable> perspectiveClass, Component component) {
+  public Issuable loadPerspective(Class<Issuable> perspectiveClass, Component component) {
     boolean supported = true;
     if (component instanceof ResourceComponent) {
       supported = Scopes.isHigherThanOrEquals(((ResourceComponent) component).scope(), Scopes.FILE);
index 996351c26f6678f00400a5fd1b45853159cc90f6..9f50e3abbfb1036167848dc434f326e01bd4ff43 100644 (file)
  */
 package org.sonar.batch.issue.tracking;
 
+import org.sonar.api.batch.fs.internal.FileMetadata;
+import org.sonar.api.batch.fs.internal.FileMetadata.LineHashConsumer;
+
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.Multimap;
 import org.apache.commons.codec.binary.Hex;
 import org.apache.commons.lang.ObjectUtils;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.batch.scan.filesystem.FileMetadata;
-import org.sonar.batch.scan.filesystem.FileMetadata.LineHashConsumer;
 
 import javax.annotation.Nullable;
 
index 6200c172993f8dcdd19ca0ae59149fcf32efeedf..e274375076b3486a05897fcaca42f96c732d5681 100644 (file)
@@ -36,7 +36,7 @@ import org.sonar.batch.bootstrapper.EnvironmentInformation;
 import org.sonar.batch.issue.tracking.ServerLineHashesLoader;
 import org.sonar.batch.protocol.input.*;
 import org.sonar.batch.protocol.input.BatchInput.ServerIssue;
-import org.sonar.batch.report.PublishReportJob;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.repository.GlobalRepositoriesLoader;
 import org.sonar.batch.repository.ProjectRepositoriesLoader;
 import org.sonar.batch.repository.ServerIssuesLoader;
@@ -62,7 +62,7 @@ public class BatchMediumTester {
   public static BatchMediumTesterBuilder builder() {
     BatchMediumTesterBuilder builder = new BatchMediumTesterBuilder().registerCoreMetrics();
     builder.bootstrapProperties.put(MEDIUM_TEST_ENABLED, "true");
-    builder.bootstrapProperties.put(PublishReportJob.KEEP_REPORT_PROP_KEY, "true");
+    builder.bootstrapProperties.put(ReportPublisher.KEEP_REPORT_PROP_KEY, "true");
     builder.bootstrapProperties.put(CoreProperties.WORKING_DIRECTORY, Files.createTempDir().getAbsolutePath());
     return builder;
   }
index 3dc913b005b0fa6c63d7a0a218e587763d26f9fc..423f430c14f2f32b7be6b05ab1110d750418b60d 100644 (file)
@@ -23,31 +23,37 @@ import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.fs.InputDir;
 import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextPointer;
+import org.sonar.api.batch.fs.TextRange;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency;
 import org.sonar.api.batch.sensor.duplication.Duplication;
 import org.sonar.api.batch.sensor.highlighting.TypeOfText;
-import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.measures.Measure;
-import org.sonar.api.source.Symbol;
 import org.sonar.batch.dependency.DependencyCache;
 import org.sonar.batch.duplication.DuplicationCache;
-import org.sonar.batch.highlighting.SyntaxHighlightingData;
 import org.sonar.batch.index.Cache.Entry;
-import org.sonar.batch.index.ComponentDataCache;
 import org.sonar.batch.issue.IssueCache;
+import org.sonar.batch.protocol.output.BatchReport.Component;
+import org.sonar.batch.protocol.output.BatchReport.Metadata;
+import org.sonar.batch.protocol.output.BatchReport.Range;
+import org.sonar.batch.protocol.output.BatchReport.Symbols.Symbol;
+import org.sonar.batch.protocol.output.BatchReport.SyntaxHighlighting.HighlightingRule;
+import org.sonar.batch.protocol.output.*;
+import org.sonar.batch.report.BatchReportUtils;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.scan.ProjectScanContainer;
 import org.sonar.batch.scan.filesystem.InputPathCache;
 import org.sonar.batch.scan.measure.MeasureCache;
-import org.sonar.batch.symbol.SymbolData;
-import org.sonar.core.source.SnapshotDataTypes;
 
 import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
 import java.io.Serializable;
 import java.util.*;
@@ -60,10 +66,10 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
   private List<org.sonar.api.batch.sensor.measure.Measure> measures = new ArrayList<>();
   private Map<String, List<Duplication>> duplications = new HashMap<>();
   private Map<String, InputFile> inputFiles = new HashMap<>();
+  private Map<String, Component> reportComponents = new HashMap<>();
   private Map<String, InputDir> inputDirs = new HashMap<>();
-  private Map<InputFile, SyntaxHighlightingData> highlightingPerFile = new HashMap<>();
-  private Map<InputFile, SymbolData> symbolTablePerFile = new HashMap<>();
   private Map<String, Map<String, Integer>> dependencies = new HashMap<>();
+  private BatchReportReader reader;
 
   @Override
   public void scanTaskCompleted(ProjectScanContainer container) {
@@ -72,16 +78,37 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
       issues.add(issue);
     }
 
+    if (!container.getComponentByType(AnalysisMode.class).isPreview()) {
+      ReportPublisher reportPublisher = container.getComponentByType(ReportPublisher.class);
+      reader = new BatchReportReader(reportPublisher.getReportDir());
+      Metadata readMetadata = getReportReader().readMetadata();
+      int rootComponentRef = readMetadata.getRootComponentRef();
+      storeReportComponents(rootComponentRef, null, readMetadata.hasBranch() ? readMetadata.getBranch() : null);
+    }
+
     storeFs(container);
 
     storeMeasures(container);
 
-    storeComponentData(container);
     storeDuplication(container);
-    // storeTestCases(container);
-    // storeCoveragePerTest(container);
     storeDependencies(container);
+  }
+
+  private void storeReportComponents(int componentRef, String parentModuleKey, @Nullable String branch) {
+    Component component = getReportReader().readComponent(componentRef);
+    if (component.hasKey()) {
+      reportComponents.put(component.getKey() + (branch != null ? ":" + branch : ""), component);
+    } else {
+      reportComponents.put(parentModuleKey + (branch != null ? ":" + branch : "") + ":" + component.getPath(), component);
+    }
+    for (int childId : component.getChildRefList()) {
+      storeReportComponents(childId, component.hasKey() ? component.getKey() : parentModuleKey, branch);
+    }
+
+  }
 
+  public BatchReportReader getReportReader() {
+    return reader;
   }
 
   private void storeMeasures(ProjectScanContainer container) {
@@ -108,20 +135,6 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
     }
   }
 
-  private void storeComponentData(ProjectScanContainer container) {
-    ComponentDataCache componentDataCache = container.getComponentByType(ComponentDataCache.class);
-    for (InputFile file : inputFiles.values()) {
-      SyntaxHighlightingData highlighting = componentDataCache.getData(((DefaultInputFile) file).key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING);
-      if (highlighting != null) {
-        highlightingPerFile.put(file, highlighting);
-      }
-      SymbolData symbolTable = componentDataCache.getData(((DefaultInputFile) file).key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING);
-      if (symbolTable != null) {
-        symbolTablePerFile.put(file, symbolTable);
-      }
-    }
-  }
-
   private void storeFs(ProjectScanContainer container) {
     InputPathCache inputFileCache = container.getComponentByType(InputPathCache.class);
     for (InputFile inputPath : inputFileCache.allFiles()) {
@@ -178,37 +191,45 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
    * Get highlighting types at a given position in an inputfile
    * @param charIndex 0-based offset in file
    */
-  public List<TypeOfText> highlightingTypeFor(InputFile file, int charIndex) {
-    SyntaxHighlightingData syntaxHighlightingData = highlightingPerFile.get(file);
-    if (syntaxHighlightingData == null) {
+  public List<TypeOfText> highlightingTypeFor(InputFile file, int line, int lineOffset) {
+    int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
+    List<HighlightingRule> syntaxHighlightingRules = getReportReader().readComponentSyntaxHighlighting(ref);
+    if (syntaxHighlightingRules.isEmpty()) {
       return Collections.emptyList();
     }
+    TextPointer pointer = file.newPointer(line, lineOffset);
     List<TypeOfText> result = new ArrayList<TypeOfText>();
-    for (SyntaxHighlightingRule sortedRule : syntaxHighlightingData.syntaxHighlightingRuleSet()) {
-      if (sortedRule.getStartPosition() <= charIndex && sortedRule.getEndPosition() > charIndex) {
-        result.add(sortedRule.getTextType());
+    for (HighlightingRule sortedRule : syntaxHighlightingRules) {
+      TextRange ruleRange = toRange(file, sortedRule.getRange());
+      if (ruleRange.start().compareTo(pointer) <= 0 && ruleRange.end().compareTo(pointer) > 0) {
+        result.add(BatchReportUtils.toBatchType(sortedRule.getType()));
       }
     }
     return result;
   }
 
+  private static TextRange toRange(InputFile file, Range reportRange) {
+    return file.newRange(file.newPointer(reportRange.getStartLine(), reportRange.getStartOffset()), file.newPointer(reportRange.getEndLine(), reportRange.getEndOffset()));
+  }
+
   /**
-   * Get list of all positions of a symbol in an inputfile
+   * Get list of all start positions of a symbol in an inputfile
    * @param symbolStartOffset 0-based start offset for the symbol in file
    * @param symbolEndOffset 0-based end offset for the symbol in file
    */
   @CheckForNull
-  public Set<Integer> symbolReferencesFor(InputFile file, int symbolStartOffset, int symbolEndOffset) {
-    SymbolData data = symbolTablePerFile.get(file);
-    if (data == null) {
-      return null;
+  public List<Range> symbolReferencesFor(InputFile file, int symbolStartLine, int symbolStartLineOffset) {
+    int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
+    List<Symbol> symbols = getReportReader().readComponentSymbols(ref);
+    if (symbols.isEmpty()) {
+      return Collections.emptyList();
     }
-    for (Symbol symbol : data.referencesBySymbol().keySet()) {
-      if (symbol.getDeclarationStartOffset() == symbolStartOffset && symbol.getDeclarationEndOffset() == symbolEndOffset) {
-        return data.referencesBySymbol().get(symbol);
+    for (Symbol symbol : symbols) {
+      if (symbol.getDeclaration().getStartLine() == symbolStartLine && symbol.getDeclaration().getStartOffset() == symbolStartLineOffset) {
+        return symbol.getReferenceList();
       }
     }
-    return null;
+    return Collections.emptyList();
   }
 
   /**
index f53ce1b715e4bb4d3576cee169c713642f49958b..644cf8bf724dfbb6160ccd6853a8875da2083f90 100644 (file)
@@ -26,7 +26,7 @@ import org.sonar.batch.events.EventBus;
 import org.sonar.batch.index.DefaultIndex;
 import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader;
 import org.sonar.batch.issue.tracking.LocalIssueTracking;
-import org.sonar.batch.report.PublishReportJob;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.rule.QProfileVerifier;
 import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
 import org.sonar.batch.scan.filesystem.FileSystemLogger;
@@ -47,12 +47,12 @@ public final class DatabaseLessPhaseExecutor implements PhaseExecutor {
   private final IssueExclusionsLoader issueExclusionsLoader;
   private final IssuesReports issuesReport;
   private final LocalIssueTracking localIssueTracking;
-  private final PublishReportJob publishReportJob;
+  private final ReportPublisher publishReportJob;
 
   public DatabaseLessPhaseExecutor(Phases phases, InitializersExecutor initializersExecutor, SensorsExecutor sensorsExecutor,
     SensorContext sensorContext, DefaultIndex index,
     EventBus eventBus, ProjectInitializer pi, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
-    IssueExclusionsLoader issueExclusionsLoader, LocalIssueTracking localIssueTracking, PublishReportJob publishReportJob) {
+    IssueExclusionsLoader issueExclusionsLoader, LocalIssueTracking localIssueTracking, ReportPublisher publishReportJob) {
     this.phases = phases;
     this.initializersExecutor = initializersExecutor;
     this.sensorsExecutor = sensorsExecutor;
index 13ec298b8d4ae4ba7eb8c9a9f4d8120a51b0a674..657a58083f2c58b36a398f481a48af83e8272fd5 100644 (file)
@@ -31,7 +31,7 @@ import org.sonar.batch.index.DefaultIndex;
 import org.sonar.batch.index.ResourcePersister;
 import org.sonar.batch.index.ScanPersister;
 import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader;
-import org.sonar.batch.report.PublishReportJob;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.rule.QProfileVerifier;
 import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
 import org.sonar.batch.scan.filesystem.FileSystemLogger;
@@ -49,7 +49,7 @@ public final class DatabaseModePhaseExecutor implements PhaseExecutor {
   private final PostJobsExecutor postJobsExecutor;
   private final InitializersExecutor initializersExecutor;
   private final SensorsExecutor sensorsExecutor;
-  private final PublishReportJob publishReportJob;
+  private final ReportPublisher publishReportJob;
   private final SensorContext sensorContext;
   private final DefaultIndex index;
   private final ProjectInitializer pi;
@@ -66,7 +66,7 @@ public final class DatabaseModePhaseExecutor implements PhaseExecutor {
   public DatabaseModePhaseExecutor(Phases phases, DecoratorsExecutor decoratorsExecutor,
     InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
     SensorContext sensorContext, DefaultIndex index,
-    EventBus eventBus, PublishReportJob publishReportJob, ProjectInitializer pi,
+    EventBus eventBus, ReportPublisher publishReportJob, ProjectInitializer pi,
     ScanPersister[] persisters, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
     IssueExclusionsLoader issueExclusionsLoader, DefaultAnalysisMode analysisMode, DatabaseSession session, ResourcePersister resourcePersister) {
     this.phases = phases;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/BatchReportUtils.java b/sonar-batch/src/main/java/org/sonar/batch/report/BatchReportUtils.java
new file mode 100644 (file)
index 0000000..4e20cd5
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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.batch.report;
+
+import org.sonar.api.batch.sensor.highlighting.TypeOfText;
+import org.sonar.batch.protocol.Constants.HighlightingType;
+
+public class BatchReportUtils {
+
+  private BatchReportUtils() {
+  }
+
+  public static HighlightingType toProtocolType(TypeOfText textType) {
+    switch (textType) {
+      case ANNOTATION:
+        return HighlightingType.ANNOTATION;
+      case COMMENT:
+        return HighlightingType.COMMENT;
+      case CONSTANT:
+        return HighlightingType.CONSTANT;
+      case CPP_DOC:
+        return HighlightingType.CPP_DOC;
+      case KEYWORD:
+        return HighlightingType.KEYWORD;
+      case KEYWORD_LIGHT:
+        return HighlightingType.KEYWORD_LIGHT;
+      case PREPROCESS_DIRECTIVE:
+        return HighlightingType.PREPROCESS_DIRECTIVE;
+      case STRING:
+        return HighlightingType.HIGHLIGHTING_STRING;
+      case STRUCTURED_COMMENT:
+        return HighlightingType.STRUCTURED_COMMENT;
+      default:
+        throw new IllegalArgumentException("Unknow highlighting type: " + textType);
+    }
+  }
+
+  public static TypeOfText toBatchType(HighlightingType type) {
+    switch (type) {
+      case ANNOTATION:
+        return TypeOfText.ANNOTATION;
+      case COMMENT:
+        return TypeOfText.COMMENT;
+      case CONSTANT:
+        return TypeOfText.CONSTANT;
+      case CPP_DOC:
+        return TypeOfText.CPP_DOC;
+      case HIGHLIGHTING_STRING:
+        return TypeOfText.STRING;
+      case KEYWORD:
+        return TypeOfText.KEYWORD;
+      case KEYWORD_LIGHT:
+        return TypeOfText.KEYWORD_LIGHT;
+      case PREPROCESS_DIRECTIVE:
+        return TypeOfText.PREPROCESS_DIRECTIVE;
+      case STRUCTURED_COMMENT:
+        return TypeOfText.STRUCTURED_COMMENT;
+      default:
+        throw new IllegalArgumentException(type + " is not a valid type");
+    }
+  }
+
+  public static String toCssClass(HighlightingType type) {
+    return toBatchType(type).cssClass();
+  }
+}
index 28dabefe4add764431d09c5344d79b98fa3f727b..7bed3e9db12299d3a5fd13a581c3481be22078c6 100644 (file)
@@ -40,7 +40,7 @@ import javax.annotation.CheckForNull;
 /**
  * Adds components and analysis metadata to output report
  */
-public class ComponentsPublisher implements ReportPublisher {
+public class ComponentsPublisher implements ReportPublisherStep {
 
   private final ResourceCache resourceCache;
   private final ProjectReactor reactor;
index 896ef5aba08eca432248a78b0ebb287af3e837fa..0295e13bb2b78ac2d88092c75dcf3dc62cdfd5d4 100644 (file)
@@ -31,7 +31,7 @@ import org.sonar.batch.protocol.output.BatchReport.Duplicate;
 import org.sonar.batch.protocol.output.BatchReport.Duplication;
 import org.sonar.batch.protocol.output.BatchReport.Range;
 
-public class DuplicationsPublisher implements ReportPublisher {
+public class DuplicationsPublisher implements ReportPublisherStep {
 
   private final ResourceCache resourceCache;
   private final DuplicationCache duplicationCache;
index 640552eccfed9fd17acb69f797d9cbedb294c04b..f2268cd20a9fc37033c9347cbb1923df4d7187a3 100644 (file)
@@ -39,7 +39,7 @@ import java.util.Collection;
 import java.util.Date;
 import java.util.Iterator;
 
-public class IssuesPublisher implements ReportPublisher {
+public class IssuesPublisher implements ReportPublisherStep {
 
   private final ResourceCache resourceCache;
   private final IssueCache issueCache;
index 8aadc2180ee18c73de02304ddeb2b63a5f578e83..0613432376b5056cf6a990e3ef639322cc47488e 100644 (file)
@@ -43,7 +43,7 @@ import javax.annotation.Nullable;
 
 import java.io.Serializable;
 
-public class MeasuresPublisher implements ReportPublisher {
+public class MeasuresPublisher implements ReportPublisherStep {
 
   private final ResourceCache resourceCache;
   private final MeasureCache measureCache;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java b/sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java
deleted file mode 100644 (file)
index 9fe5d4b..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.batch.report;
-
-import com.github.kevinsawicki.http.HttpRequest;
-import com.google.common.annotations.VisibleForTesting;
-import org.apache.commons.io.FileUtils;
-import org.codehaus.plexus.personality.plexus.lifecycle.phase.Startable;
-import org.codehaus.plexus.personality.plexus.lifecycle.phase.StartingException;
-import org.codehaus.plexus.personality.plexus.lifecycle.phase.StoppingException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.BatchComponent;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.api.config.Settings;
-import org.sonar.api.platform.Server;
-import org.sonar.api.utils.TempFolder;
-import org.sonar.api.utils.ZipUtils;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
-import org.sonar.batch.bootstrap.ServerClient;
-import org.sonar.batch.protocol.output.BatchReportWriter;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-public class PublishReportJob implements BatchComponent, Startable {
-
-  private static final Logger LOG = LoggerFactory.getLogger(PublishReportJob.class);
-  public static final String KEEP_REPORT_PROP_KEY = "sonar.batch.keepReport";
-
-  private final ServerClient serverClient;
-  private final Server server;
-  private final Settings settings;
-  private final ProjectReactor projectReactor;
-  private final DefaultAnalysisMode analysisMode;
-  private final TempFolder temp;
-
-  private ReportPublisher[] publishers;
-
-  private File reportDir;
-  private BatchReportWriter writer;
-
-  public PublishReportJob(Settings settings, ServerClient serverClient, Server server,
-    ProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisher[] publishers) {
-    this.serverClient = serverClient;
-    this.server = server;
-    this.projectReactor = projectReactor;
-    this.settings = settings;
-    this.analysisMode = analysisMode;
-    this.temp = temp;
-    this.publishers = publishers;
-  }
-
-  @Override
-  public void start() throws StartingException {
-    reportDir = new File(projectReactor.getRoot().getWorkDir(), "batch-report");
-    writer = new BatchReportWriter(reportDir);
-  }
-
-  @Override
-  public void stop() throws StoppingException {
-    if (!settings.getBoolean(KEEP_REPORT_PROP_KEY)) {
-      FileUtils.deleteQuietly(reportDir);
-    } else {
-      LOG.info("Batch report generated in " + reportDir);
-    }
-  }
-
-  public File getReportDir() {
-    return reportDir;
-  }
-
-  public BatchReportWriter getWriter() {
-    return writer;
-  }
-
-  public void execute() {
-    // If this is a preview analysis then we should not upload reports
-    if (!analysisMode.isPreview()) {
-      File report = prepareReport();
-      if (!analysisMode.isMediumTest()) {
-        uploadMultiPartReport(report);
-      }
-    }
-    logSuccess(LoggerFactory.getLogger(getClass()));
-  }
-
-  private File prepareReport() {
-    try {
-      long startTime = System.currentTimeMillis();
-      for (ReportPublisher publisher : publishers) {
-        publisher.publish(writer);
-      }
-      long stopTime = System.currentTimeMillis();
-      LOG.info("Analysis reports generated in " + (stopTime - startTime) + "ms, dir size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(reportDir)));
-
-      startTime = System.currentTimeMillis();
-      File reportZip = temp.newFile("batch-report", ".zip");
-      ZipUtils.zipDir(reportDir, reportZip);
-      stopTime = System.currentTimeMillis();
-      LOG.info("Analysis reports compressed in " + (stopTime - startTime) + "ms, zip size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(reportZip)));
-      return reportZip;
-    } catch (IOException e) {
-      throw new IllegalStateException("Unable to prepare batch report", e);
-    }
-  }
-
-  @VisibleForTesting
-  void uploadMultiPartReport(File report) {
-    LOG.debug("Publish results");
-    long startTime = System.currentTimeMillis();
-    URL url;
-    try {
-      String effectiveKey = projectReactor.getRoot().getKeyWithBranch();
-      url = new URL(serverClient.getURL() + "/api/computation/submit_report?projectKey=" + effectiveKey);
-    } catch (MalformedURLException e) {
-      throw new IllegalArgumentException("Invalid URL", e);
-    }
-    HttpRequest request = HttpRequest.post(url);
-    request.trustAllCerts();
-    request.trustAllHosts();
-    request.header("User-Agent", String.format("SonarQube %s", server.getVersion()));
-    request.basic(serverClient.getLogin(), serverClient.getPassword());
-    request.part("report", null, "application/octet-stream", report);
-    if (!request.ok()) {
-      int responseCode = request.code();
-      if (responseCode == 401) {
-        throw new IllegalStateException(String.format(serverClient.getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD));
-      }
-      if (responseCode == 403) {
-        // SONAR-4397 Details are in response content
-        throw new IllegalStateException(request.body());
-      }
-      throw new IllegalStateException(String.format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, request.body()));
-    }
-    long stopTime = System.currentTimeMillis();
-    LOG.info("Analysis reports sent to server in " + (stopTime - startTime) + "ms");
-  }
-
-  @VisibleForTesting
-  void logSuccess(Logger logger) {
-    if (analysisMode.isPreview() || analysisMode.isMediumTest()) {
-      logger.info("ANALYSIS SUCCESSFUL");
-
-    } else {
-      String baseUrl = settings.getString(CoreProperties.SERVER_BASE_URL);
-      if (baseUrl.equals(settings.getDefaultValue(CoreProperties.SERVER_BASE_URL))) {
-        // If server base URL was not configured in Sonar server then is is better to take URL configured on batch side
-        baseUrl = serverClient.getURL();
-      }
-      if (!baseUrl.endsWith("/")) {
-        baseUrl += "/";
-      }
-      String effectiveKey = projectReactor.getRoot().getKeyWithBranch();
-      String url = baseUrl + "dashboard/index/" + effectiveKey;
-      logger.info("ANALYSIS SUCCESSFUL, you can browse {}", url);
-      logger.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report.");
-    }
-  }
-}
index 07aa03fccbeae44349add5c307ee61b4062fcd47..2043fcd7f01e81e67698e64ecf1a09b533e64e44 100644 (file)
  */
 package org.sonar.batch.report;
 
+import com.github.kevinsawicki.http.HttpRequest;
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.io.FileUtils;
+import org.codehaus.plexus.personality.plexus.lifecycle.phase.Startable;
+import org.codehaus.plexus.personality.plexus.lifecycle.phase.StartingException;
+import org.codehaus.plexus.personality.plexus.lifecycle.phase.StoppingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.config.Settings;
+import org.sonar.api.platform.Server;
+import org.sonar.api.utils.TempFolder;
+import org.sonar.api.utils.ZipUtils;
+import org.sonar.batch.bootstrap.DefaultAnalysisMode;
+import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.protocol.output.BatchReportWriter;
 
-/**
- * Adds a sub-part of data to output report
- */
-public interface ReportPublisher {
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class ReportPublisher implements BatchComponent, Startable {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ReportPublisher.class);
+  public static final String KEEP_REPORT_PROP_KEY = "sonar.batch.keepReport";
+
+  private final ServerClient serverClient;
+  private final Server server;
+  private final Settings settings;
+  private final ProjectReactor projectReactor;
+  private final DefaultAnalysisMode analysisMode;
+  private final TempFolder temp;
+
+  private ReportPublisherStep[] publishers;
+
+  private File reportDir;
+  private BatchReportWriter writer;
+
+  public ReportPublisher(Settings settings, ServerClient serverClient, Server server,
+    ProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) {
+    this.serverClient = serverClient;
+    this.server = server;
+    this.projectReactor = projectReactor;
+    this.settings = settings;
+    this.analysisMode = analysisMode;
+    this.temp = temp;
+    this.publishers = publishers;
+  }
+
+  @Override
+  public void start() throws StartingException {
+    reportDir = new File(projectReactor.getRoot().getWorkDir(), "batch-report");
+    writer = new BatchReportWriter(reportDir);
+  }
+
+  @Override
+  public void stop() throws StoppingException {
+    if (!settings.getBoolean(KEEP_REPORT_PROP_KEY)) {
+      FileUtils.deleteQuietly(reportDir);
+    } else {
+      LOG.info("Batch report generated in " + reportDir);
+    }
+  }
+
+  public File getReportDir() {
+    return reportDir;
+  }
+
+  public BatchReportWriter getWriter() {
+    return writer;
+  }
+
+  public void execute() {
+    // If this is a preview analysis then we should not upload reports
+    if (!analysisMode.isPreview()) {
+      File report = prepareReport();
+      if (!analysisMode.isMediumTest()) {
+        uploadMultiPartReport(report);
+      }
+    }
+    logSuccess(LoggerFactory.getLogger(getClass()));
+  }
+
+  private File prepareReport() {
+    try {
+      long startTime = System.currentTimeMillis();
+      for (ReportPublisherStep publisher : publishers) {
+        publisher.publish(writer);
+      }
+      long stopTime = System.currentTimeMillis();
+      LOG.info("Analysis reports generated in " + (stopTime - startTime) + "ms, dir size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(reportDir)));
+
+      startTime = System.currentTimeMillis();
+      File reportZip = temp.newFile("batch-report", ".zip");
+      ZipUtils.zipDir(reportDir, reportZip);
+      stopTime = System.currentTimeMillis();
+      LOG.info("Analysis reports compressed in " + (stopTime - startTime) + "ms, zip size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(reportZip)));
+      return reportZip;
+    } catch (IOException e) {
+      throw new IllegalStateException("Unable to prepare batch report", e);
+    }
+  }
+
+  @VisibleForTesting
+  void uploadMultiPartReport(File report) {
+    LOG.debug("Publish results");
+    long startTime = System.currentTimeMillis();
+    URL url;
+    try {
+      String effectiveKey = projectReactor.getRoot().getKeyWithBranch();
+      url = new URL(serverClient.getURL() + "/api/computation/submit_report?projectKey=" + effectiveKey);
+    } catch (MalformedURLException e) {
+      throw new IllegalArgumentException("Invalid URL", e);
+    }
+    HttpRequest request = HttpRequest.post(url);
+    request.trustAllCerts();
+    request.trustAllHosts();
+    request.header("User-Agent", String.format("SonarQube %s", server.getVersion()));
+    request.basic(serverClient.getLogin(), serverClient.getPassword());
+    request.part("report", null, "application/octet-stream", report);
+    if (!request.ok()) {
+      int responseCode = request.code();
+      if (responseCode == 401) {
+        throw new IllegalStateException(String.format(serverClient.getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD));
+      }
+      if (responseCode == 403) {
+        // SONAR-4397 Details are in response content
+        throw new IllegalStateException(request.body());
+      }
+      throw new IllegalStateException(String.format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, request.body()));
+    }
+    long stopTime = System.currentTimeMillis();
+    LOG.info("Analysis reports sent to server in " + (stopTime - startTime) + "ms");
+  }
 
-  void publish(BatchReportWriter writer);
+  @VisibleForTesting
+  void logSuccess(Logger logger) {
+    if (analysisMode.isPreview() || analysisMode.isMediumTest()) {
+      logger.info("ANALYSIS SUCCESSFUL");
 
+    } else {
+      String baseUrl = settings.getString(CoreProperties.SERVER_BASE_URL);
+      if (baseUrl.equals(settings.getDefaultValue(CoreProperties.SERVER_BASE_URL))) {
+        // If server base URL was not configured in Sonar server then is is better to take URL configured on batch side
+        baseUrl = serverClient.getURL();
+      }
+      if (!baseUrl.endsWith("/")) {
+        baseUrl += "/";
+      }
+      String effectiveKey = projectReactor.getRoot().getKeyWithBranch();
+      String url = baseUrl + "dashboard/index/" + effectiveKey;
+      logger.info("ANALYSIS SUCCESSFUL, you can browse {}", url);
+      logger.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report.");
+    }
+  }
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisherStep.java b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisherStep.java
new file mode 100644 (file)
index 0000000..eb771b3
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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.batch.report;
+
+import org.sonar.batch.protocol.output.BatchReportWriter;
+
+/**
+ * Adds a sub-part of data to output report
+ */
+public interface ReportPublisherStep {
+
+  void publish(BatchReportWriter writer);
+
+}
index 5df7f4c665d975cd001914755a0193803a6bcbfd..6e724866c674f67da049f438fbf9c305771822e1 100644 (file)
@@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchComponent;
 import org.sonar.api.batch.InstantiationStrategy;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.batch.rule.CheckFactory;
 import org.sonar.api.checks.NoSonarFilter;
 import org.sonar.api.platform.ComponentContainer;
@@ -37,6 +38,7 @@ import org.sonar.batch.deprecated.DeprecatedSensorContext;
 import org.sonar.batch.deprecated.ResourceFilters;
 import org.sonar.batch.deprecated.components.DefaultProjectClasspath;
 import org.sonar.batch.deprecated.components.DefaultTimeMachine;
+import org.sonar.batch.deprecated.perspectives.BatchPerspectives;
 import org.sonar.batch.events.EventBus;
 import org.sonar.batch.index.DefaultIndex;
 import org.sonar.batch.issue.IssuableFactory;
@@ -63,7 +65,8 @@ import org.sonar.batch.sensor.AnalyzerOptimizer;
 import org.sonar.batch.sensor.DefaultSensorContext;
 import org.sonar.batch.sensor.DefaultSensorStorage;
 import org.sonar.batch.sensor.coverage.CoverageExclusions;
-import org.sonar.core.component.ScanPerspectives;
+import org.sonar.batch.source.HighlightableBuilder;
+import org.sonar.batch.source.SymbolizableBuilder;
 
 public class ModuleScanContainer extends ComponentContainer {
   private static final Logger LOG = LoggerFactory.getLogger(ModuleScanContainer.class);
@@ -111,7 +114,7 @@ public class ModuleScanContainer extends ComponentContainer {
       SensorsExecutor.class,
       InitializersExecutor.class,
       ProjectInitializer.class,
-      PublishReportJob.class,
+      ReportPublisher.class,
       ComponentsPublisher.class,
       IssuesPublisher.class,
       MeasuresPublisher.class,
@@ -172,7 +175,10 @@ public class ModuleScanContainer extends ComponentContainer {
       IgnoreIssuesFilter.class,
       NoSonarFilter.class,
 
-      ScanPerspectives.class);
+      // Perspectives
+      BatchPerspectives.class,
+      HighlightableBuilder.class,
+      SymbolizableBuilder.class);
   }
 
   private void addDataBaseComponents() {
index aaa63f0d3286e1782adb072fb33798d0b35871da..30bfafa97931b2f43210d74beafac022c618e7a0 100644 (file)
@@ -59,8 +59,6 @@ import org.sonar.batch.rule.RulesProvider;
 import org.sonar.batch.scan.filesystem.InputPathCache;
 import org.sonar.batch.scan.measure.MeasureCache;
 import org.sonar.batch.source.CodeColorizers;
-import org.sonar.batch.source.HighlightableBuilder;
-import org.sonar.batch.source.SymbolizableBuilder;
 import org.sonar.core.component.ScanGraph;
 import org.sonar.core.issue.IssueUpdater;
 import org.sonar.core.issue.workflow.FunctionExecutor;
@@ -131,7 +129,6 @@ public class ProjectScanContainer extends ComponentContainer {
       DefaultFileLinesContextFactory.class,
       Caches.class,
       ResourceCache.class,
-      ComponentDataCache.class,
       SourceDataFactory.class,
 
       // file system
@@ -162,8 +159,6 @@ public class ProjectScanContainer extends ComponentContainer {
       // lang
       Languages.class,
       DefaultLanguagesRepository.class,
-      HighlightableBuilder.class,
-      SymbolizableBuilder.class,
 
       // Differential periods
       PeriodsDefinition.class,
@@ -187,7 +182,7 @@ public class ProjectScanContainer extends ComponentContainer {
       ProjectSettings.class,
 
       // Report
-      PublishReportJob.class,
+      ReportPublisher.class,
       ComponentsPublisher.class,
       IssuesPublisher.class,
       MeasuresPublisher.class,
index 460a08831b34b0d5425756f13691cc04fe716f7c..a30037a1c00ed753ba3d4f429600b4b1b5856ec5 100644 (file)
@@ -26,6 +26,7 @@ import org.sonar.api.resources.File;
 import org.sonar.api.resources.Languages;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
+import org.sonar.batch.index.ResourceCache;
 import org.sonar.batch.index.ResourceKeyMigration;
 import org.sonar.batch.index.ResourcePersister;
 
@@ -43,18 +44,20 @@ public class ComponentIndexer implements BatchComponent {
   private final ResourceKeyMigration migration;
   private final Project module;
   private final ResourcePersister resourcePersister;
+  private final ResourceCache resourceCache;
 
-  public ComponentIndexer(Project module, Languages languages, SonarIndex sonarIndex, @Nullable ResourceKeyMigration migration,
+  public ComponentIndexer(Project module, Languages languages, SonarIndex sonarIndex, ResourceCache resourceCache, @Nullable ResourceKeyMigration migration,
     @Nullable ResourcePersister resourcePersister) {
     this.module = module;
     this.languages = languages;
     this.sonarIndex = sonarIndex;
+    this.resourceCache = resourceCache;
     this.migration = migration;
     this.resourcePersister = resourcePersister;
   }
 
-  public ComponentIndexer(Project module, Languages languages, SonarIndex sonarIndex) {
-    this(module, languages, sonarIndex, null, null);
+  public ComponentIndexer(Project module, Languages languages, SonarIndex sonarIndex, ResourceCache resourceCache) {
+    this(module, languages, sonarIndex, resourceCache, null, null);
   }
 
   public void execute(DefaultModuleFileSystem fs) {
@@ -74,6 +77,7 @@ public class ComponentIndexer implements BatchComponent {
       boolean unitTest = InputFile.Type.TEST == inputFile.type();
       Resource sonarFile = File.create(inputFile.relativePath(), languages.get(languageKey), unitTest);
       sonarIndex.index(sonarFile);
+      resourceCache.get(sonarFile).setInputPath(inputFile);
     }
 
     if (resourcePersister != null) {
@@ -81,5 +85,4 @@ public class ComponentIndexer implements BatchComponent {
       resourcePersister.persist();
     }
   }
-
 }
index c5eb6bef9b40cb0a011ff38c32a177e63a5dbd09..fbc40556475f53eab32e41685ff00c02d04646c6 100644 (file)
@@ -37,17 +37,8 @@ import org.sonar.batch.util.ProgressReport;
 
 import java.io.File;
 import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
+import java.util.*;
+import java.util.concurrent.*;
 
 /**
  * Index input files into {@link InputPathCache}.
@@ -63,15 +54,13 @@ public class FileIndexer implements BatchComponent {
   private final boolean isAggregator;
   private final ExclusionFilters exclusionFilters;
   private final InputFileBuilderFactory inputFileBuilderFactory;
-  private final InputPathCache inputPathCache;
 
   private ProgressReport progressReport;
   private ExecutorService executorService;
   private List<Future<Void>> tasks;
 
   public FileIndexer(List<InputFileFilter> filters, ExclusionFilters exclusionFilters, InputFileBuilderFactory inputFileBuilderFactory,
-    ProjectDefinition def, InputPathCache inputPathCache) {
-    this.inputPathCache = inputPathCache;
+    ProjectDefinition def) {
     this.filters = filters;
     this.exclusionFilters = exclusionFilters;
     this.inputFileBuilderFactory = inputFileBuilderFactory;
@@ -154,12 +143,11 @@ public class FileIndexer implements BatchComponent {
     tasks.add(executorService.submit(new Callable<Void>() {
       @Override
       public Void call() {
-        InputFileMetadata metadata = inputFileBuilder.completeAndComputeMetadata(inputFile, type);
-        if (metadata != null && accept(inputFile)) {
-          fs.add(inputFile);
-          status.markAsIndexed(inputFile);
-          inputPathCache.put(inputFile.moduleKey(), inputFile.relativePath(), metadata);
-          File parentDir = inputFile.file().getParentFile();
+        DeprecatedDefaultInputFile completedInputFile = inputFileBuilder.completeAndComputeMetadata(inputFile, type);
+        if (completedInputFile != null && accept(completedInputFile)) {
+          fs.add(completedInputFile);
+          status.markAsIndexed(completedInputFile);
+          File parentDir = completedInputFile.file().getParentFile();
           String relativePath = new PathResolver().relativePath(fs.baseDir(), parentDir);
           if (relativePath != null) {
             DefaultInputDir inputDir = new DefaultInputDir(fs.moduleKey(), relativePath);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileMetadata.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileMetadata.java
deleted file mode 100644 (file)
index 0516174..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * 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.batch.scan.filesystem;
-
-import com.google.common.base.Charsets;
-import com.google.common.primitives.Ints;
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.io.ByteOrderMark;
-import org.apache.commons.io.input.BOMInputStream;
-import org.sonar.api.BatchComponent;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.AnalysisMode;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
-import java.io.*;
-import java.nio.charset.Charset;
-import java.security.MessageDigest;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Computes hash of files. Ends of Lines are ignored, so files with
- * same content but different EOL encoding have the same hash.
- */
-public class FileMetadata implements BatchComponent {
-
-  private static final Logger LOG = Loggers.get(FileMetadata.class);
-
-  private static final char LINE_FEED = '\n';
-  private static final char CARRIAGE_RETURN = '\r';
-  private final AnalysisMode analysisMode;
-
-  public FileMetadata(AnalysisMode analysisMode) {
-    this.analysisMode = analysisMode;
-  }
-
-  private abstract static class CharHandler {
-
-    void handleAll(char c) {
-    }
-
-    void handleIgnoreEoL(char c) {
-    }
-
-    void newLine() {
-    }
-
-    void eof() {
-    }
-  }
-
-  private static class LineCounter extends CharHandler {
-    private boolean empty = true;
-    private int lines = 1;
-    private int nonBlankLines = 0;
-    private boolean blankLine = true;
-    boolean alreadyLoggedInvalidCharacter = false;
-    private final File file;
-    private final Charset encoding;
-
-    LineCounter(File file, Charset encoding) {
-      this.file = file;
-      this.encoding = encoding;
-    }
-
-    @Override
-    void handleAll(char c) {
-      this.empty = false;
-      if (!alreadyLoggedInvalidCharacter && c == '\ufffd') {
-        LOG.warn("Invalid character encountered in file " + file + " at line " + lines
-          + " for encoding " + encoding + ". Please fix file content or configure the encoding to be used using property '" + CoreProperties.ENCODING_PROPERTY + "'.");
-        alreadyLoggedInvalidCharacter = true;
-      }
-    }
-
-    @Override
-    void newLine() {
-      lines++;
-      if (!blankLine) {
-        nonBlankLines++;
-      }
-      blankLine = true;
-    }
-
-    @Override
-    void handleIgnoreEoL(char c) {
-      if (!Character.isWhitespace(c)) {
-        blankLine = false;
-      }
-    }
-
-    @Override
-    void eof() {
-      if (!blankLine) {
-        nonBlankLines++;
-      }
-    }
-
-    public int lines() {
-      return lines;
-    }
-
-    public int nonBlankLines() {
-      return nonBlankLines;
-    }
-
-    public boolean isEmpty() {
-      return empty;
-    }
-  }
-
-  private static class FileHashComputer extends CharHandler {
-    private MessageDigest globalMd5Digest = DigestUtils.getMd5Digest();
-    private StringBuilder sb = new StringBuilder();
-
-    @Override
-    void handleIgnoreEoL(char c) {
-      sb.append(c);
-    }
-
-    @Override
-    void newLine() {
-      sb.append(LINE_FEED);
-      globalMd5Digest.update(sb.toString().getBytes(Charsets.UTF_8));
-      sb.setLength(0);
-    }
-
-    @Override
-    void eof() {
-      if (sb.length() > 0) {
-        globalMd5Digest.update(sb.toString().getBytes(Charsets.UTF_8));
-      }
-    }
-
-    @CheckForNull
-    public String getHash() {
-      return Hex.encodeHexString(globalMd5Digest.digest());
-    }
-  }
-
-  private static class LineHashComputer extends CharHandler {
-    private final MessageDigest lineMd5Digest = DigestUtils.getMd5Digest();
-    private final StringBuilder sb = new StringBuilder();
-    private final LineHashConsumer consumer;
-    private int line = 1;
-
-    public LineHashComputer(LineHashConsumer consumer) {
-      this.consumer = consumer;
-    }
-
-    @Override
-    void handleIgnoreEoL(char c) {
-      if (!Character.isWhitespace(c)) {
-        sb.append(c);
-      }
-    }
-
-    @Override
-    void newLine() {
-      consumer.consume(line, sb.length() > 0 ? lineMd5Digest.digest(sb.toString().getBytes(Charsets.UTF_8)) : null);
-      sb.setLength(0);
-      line++;
-    }
-
-    @Override
-    void eof() {
-      consumer.consume(line, sb.length() > 0 ? lineMd5Digest.digest(sb.toString().getBytes(Charsets.UTF_8)) : null);
-    }
-
-  }
-
-  private static class LineOffsetCounter extends CharHandler {
-    private int currentOriginalOffset = 0;
-    private List<Integer> originalLineOffsets = new ArrayList<Integer>();
-    private int lastValidOffset = 0;
-
-    public LineOffsetCounter() {
-      originalLineOffsets.add(0);
-    }
-
-    @Override
-    void handleAll(char c) {
-      currentOriginalOffset++;
-    }
-
-    @Override
-    void newLine() {
-      originalLineOffsets.add(currentOriginalOffset);
-    }
-
-    @Override
-    void eof() {
-      lastValidOffset = currentOriginalOffset;
-    }
-
-    public List<Integer> getOriginalLineOffsets() {
-      return originalLineOffsets;
-    }
-
-    public int getLastValidOffset() {
-      return lastValidOffset;
-    }
-
-  }
-
-  /**
-   * Compute hash of a file ignoring line ends differences.
-   * Maximum performance is needed.
-   */
-  Metadata read(File file, Charset encoding) {
-    LineCounter lineCounter = new LineCounter(file, encoding);
-    FileHashComputer fileHashComputer = new FileHashComputer();
-    LineOffsetCounter lineOffsetCounter = new LineOffsetCounter();
-    if (!analysisMode.isPreview()) {
-      scanFile(file, encoding, lineCounter, fileHashComputer, lineOffsetCounter);
-    } else {
-      // No need to compute line offsets in preview mode since there is no syntax highlighting
-      scanFile(file, encoding, lineCounter, fileHashComputer);
-    }
-    return new Metadata(lineCounter.lines(), lineCounter.nonBlankLines(), fileHashComputer.getHash(), lineOffsetCounter.getOriginalLineOffsets(),
-      lineOffsetCounter.getLastValidOffset(),
-      lineCounter.isEmpty());
-  }
-
-  private static void scanFile(File file, Charset encoding, CharHandler... handlers) {
-    char c = (char) 0;
-    try (BOMInputStream bomIn = new BOMInputStream(new FileInputStream(file),
-      ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE);
-      Reader reader = new BufferedReader(new InputStreamReader(bomIn, encoding))) {
-      int i = reader.read();
-      boolean afterCR = false;
-      while (i != -1) {
-        c = (char) i;
-        if (afterCR) {
-          for (CharHandler handler : handlers) {
-            if (c != CARRIAGE_RETURN && c != LINE_FEED) {
-              handler.handleIgnoreEoL(c);
-            }
-            handler.handleAll(c);
-            handler.newLine();
-          }
-          afterCR = c == CARRIAGE_RETURN;
-        } else if (c == LINE_FEED) {
-          for (CharHandler handler : handlers) {
-            handler.handleAll(c);
-            handler.newLine();
-          }
-        } else if (c == CARRIAGE_RETURN) {
-          afterCR = true;
-          for (CharHandler handler : handlers) {
-            handler.handleAll(c);
-          }
-        } else {
-          for (CharHandler handler : handlers) {
-            handler.handleIgnoreEoL(c);
-            handler.handleAll(c);
-          }
-        }
-        i = reader.read();
-      }
-      for (CharHandler handler : handlers) {
-        handler.eof();
-      }
-    } catch (IOException e) {
-      throw new IllegalStateException(String.format("Fail to read file '%s' with encoding '%s'", file.getAbsolutePath(), encoding), e);
-    }
-  }
-
-  static class Metadata {
-    final int lines;
-    final int nonBlankLines;
-    final String hash;
-    final int[] originalLineOffsets;
-    final int lastValidOffset;
-    final boolean empty;
-
-    private Metadata(int lines, int nonBlankLines, String hash, List<Integer> originalLineOffsets, int lastValidOffset, boolean empty) {
-      this.lines = lines;
-      this.nonBlankLines = nonBlankLines;
-      this.hash = hash;
-      this.empty = empty;
-      this.originalLineOffsets = Ints.toArray(originalLineOffsets);
-      this.lastValidOffset = lastValidOffset;
-    }
-  }
-
-  public static interface LineHashConsumer {
-
-    void consume(int lineIdx, @Nullable byte[] hash);
-
-  }
-
-  /**
-   * Compute a MD5 hash of each line of the file after removing of all blank chars
-   */
-  public static void computeLineHashesForIssueTracking(DefaultInputFile f, LineHashConsumer consumer) {
-    scanFile(f.file(), f.charset(), new LineHashComputer(consumer));
-  }
-}
index 2077e91d578312fa4a77dd46629790e6b076891c..e58d56252961d1f9653c40308f3056ee4c699b03 100644 (file)
@@ -25,6 +25,7 @@ import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.fs.FileSystem;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.config.Settings;
 import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.batch.bootstrap.DefaultAnalysisMode;
@@ -92,7 +93,7 @@ class InputFileBuilder {
    * Optimization to not compute InputFile metadata if the file is excluded from analysis.
    */
   @CheckForNull
-  InputFileMetadata completeAndComputeMetadata(DeprecatedDefaultInputFile inputFile, InputFile.Type type) {
+  DeprecatedDefaultInputFile completeAndComputeMetadata(DeprecatedDefaultInputFile inputFile, InputFile.Type type) {
     inputFile.setType(type);
     inputFile.setModuleBaseDir(fs.baseDir().toPath());
     inputFile.setCharset(fs.encoding());
@@ -103,22 +104,13 @@ class InputFileBuilder {
     }
     inputFile.setLanguage(lang);
 
-    InputFileMetadata result = new InputFileMetadata();
+    inputFile.initMetadata(fileMetadata.readMetadata(inputFile.file(), fs.encoding()));
 
-    FileMetadata.Metadata metadata = fileMetadata.read(inputFile.file(), fs.encoding());
-    inputFile.setLines(metadata.lines);
-    inputFile.setLastValidOffset(metadata.lastValidOffset);
-
-    result.setNonBlankLines(metadata.nonBlankLines);
-    result.setHash(metadata.hash);
-    result.setOriginalLineOffsets(metadata.originalLineOffsets);
-    result.setEmpty(metadata.empty);
-
-    inputFile.setStatus(statusDetection.status(inputFile.moduleKey(), inputFile.relativePath(), metadata.hash));
+    inputFile.setStatus(statusDetection.status(inputFile.moduleKey(), inputFile.relativePath(), inputFile.hash()));
     if (analysisMode.isIncremental() && inputFile.status() == InputFile.Status.SAME) {
       return null;
     }
-    return result;
+    return inputFile;
   }
 
 }
index c173975fc532b905b0d1eac9d350424bcd631456..74685bd79557cf6e8da36bdc102474aafebb22af 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.batch.scan.filesystem;
 
+import org.sonar.api.batch.fs.internal.FileMetadata;
+
 import org.sonar.api.BatchComponent;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.config.Settings;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileMetadata.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileMetadata.java
deleted file mode 100644 (file)
index f37c672..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.batch.scan.filesystem;
-
-import java.io.Serializable;
-
-/**
- * Additional input file metadata that are stored in a disk storage to save memory
- */
-public class InputFileMetadata implements Serializable {
-
-  private String hash;
-  private int nonBlankLines;
-  private int[] originalLineOffsets;
-  private boolean empty;
-
-  /**
-   * Digest hash of the file.
-   */
-  public String hash() {
-    return hash;
-  }
-
-  public int nonBlankLines() {
-    return nonBlankLines;
-  }
-
-  public int[] originalLineOffsets() {
-    return originalLineOffsets;
-  }
-
-  public InputFileMetadata setHash(String hash) {
-    this.hash = hash;
-    return this;
-  }
-
-  public InputFileMetadata setNonBlankLines(int nonBlankLines) {
-    this.nonBlankLines = nonBlankLines;
-    return this;
-  }
-
-  public InputFileMetadata setOriginalLineOffsets(int[] originalLineOffsets) {
-    this.originalLineOffsets = originalLineOffsets;
-    return this;
-  }
-
-  public boolean isEmpty() {
-    return this.empty;
-  }
-
-  public InputFileMetadata setEmpty(boolean empty) {
-    this.empty = empty;
-    return this;
-  }
-
-}
index 22cbe41124f53a44f6ca31a0a4274b2e32624f3d..92423f3a25dbee87373a38497eefc75a68d21c21 100644 (file)
@@ -25,7 +25,6 @@ import org.sonar.api.BatchComponent;
 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.internal.DefaultInputFile;
 import org.sonar.batch.index.BatchResource;
 
 import javax.annotation.CheckForNull;
@@ -40,7 +39,6 @@ public class InputPathCache implements BatchComponent {
 
   private final Map<String, SortedMap<String, InputFile>> inputFileCache = new LinkedHashMap<>();
   private final Map<String, SortedMap<String, InputDir>> inputDirCache = new LinkedHashMap<>();
-  private final Map<String, Map<String, InputFileMetadata>> inputFileMetadataCache = new LinkedHashMap<>();
 
   public Iterable<InputFile> allFiles() {
     return Iterables.concat(Iterables.transform(inputFileCache.values(), new Function<Map<String, InputFile>, Collection<InputFile>>() {
@@ -77,7 +75,6 @@ public class InputPathCache implements BatchComponent {
   public InputPathCache removeModule(String moduleKey) {
     inputFileCache.remove(moduleKey);
     inputDirCache.remove(moduleKey);
-    inputFileMetadataCache.remove(moduleKey);
     return this;
   }
 
@@ -85,9 +82,6 @@ public class InputPathCache implements BatchComponent {
     if (inputFileCache.containsKey(moduleKey)) {
       inputFileCache.get(moduleKey).remove(inputFile.relativePath());
     }
-    if (inputFileMetadataCache.containsKey(moduleKey)) {
-      inputFileMetadataCache.get(moduleKey).remove(inputFile.relativePath());
-    }
     return this;
   }
 
@@ -106,14 +100,6 @@ public class InputPathCache implements BatchComponent {
     return this;
   }
 
-  public synchronized InputPathCache put(String moduleKey, String relativePath, InputFileMetadata metadata) {
-    if (!inputFileMetadataCache.containsKey(moduleKey)) {
-      inputFileMetadataCache.put(moduleKey, new HashMap<String, InputFileMetadata>());
-    }
-    inputFileMetadataCache.get(moduleKey).put(relativePath, metadata);
-    return this;
-  }
-
   public InputPathCache put(String moduleKey, InputDir inputDir) {
     if (!inputDirCache.containsKey(moduleKey)) {
       inputDirCache.put(moduleKey, new TreeMap<String, InputDir>());
@@ -130,18 +116,6 @@ public class InputPathCache implements BatchComponent {
     return null;
   }
 
-  @CheckForNull
-  public InputFileMetadata getFileMetadata(String moduleKey, String relativePath) {
-    if (inputFileMetadataCache.containsKey(moduleKey)) {
-      return inputFileMetadataCache.get(moduleKey).get(relativePath);
-    }
-    return null;
-  }
-
-  public InputFileMetadata getFileMetadata(DefaultInputFile inputFile) {
-    return getFileMetadata(inputFile.moduleKey(), inputFile.relativePath());
-  }
-
   @CheckForNull
   public InputDir getDir(String moduleKey, String relativePath) {
     if (inputDirCache.containsKey(moduleKey)) {
index 8795742ba5b4c8ce28d954515d66490c699a215a..54fd33a6f40a9636550db5cb05121e640e55f569 100644 (file)
@@ -33,8 +33,7 @@ import org.sonar.api.batch.sensor.SensorDescriptor;
 import org.sonar.batch.index.ResourceCache;
 import org.sonar.batch.protocol.input.FileData;
 import org.sonar.batch.protocol.input.ProjectRepositories;
-import org.sonar.batch.report.PublishReportJob;
-import org.sonar.batch.scan.filesystem.InputFileMetadata;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.scan.filesystem.InputPathCache;
 
 import java.util.LinkedList;
@@ -48,18 +47,16 @@ public final class ScmSensor implements Sensor {
   private final ScmConfiguration configuration;
   private final FileSystem fs;
   private final ProjectRepositories projectReferentials;
-  private final InputPathCache inputPathCache;
   private final ResourceCache resourceCache;
-  private final PublishReportJob publishReportJob;
+  private final ReportPublisher publishReportJob;
 
   public ScmSensor(ProjectDefinition projectDefinition, ScmConfiguration configuration,
     ProjectRepositories projectReferentials, FileSystem fs, InputPathCache inputPathCache, ResourceCache resourceCache,
-    PublishReportJob publishReportJob) {
+    ReportPublisher publishReportJob) {
     this.projectDefinition = projectDefinition;
     this.configuration = configuration;
     this.projectReferentials = projectReferentials;
     this.fs = fs;
-    this.inputPathCache = inputPathCache;
     this.resourceCache = resourceCache;
     this.publishReportJob = publishReportJob;
   }
@@ -96,7 +93,7 @@ public final class ScmSensor implements Sensor {
       LOG.warn("Forced reloading of SCM data for all files.");
     }
     List<InputFile> filesToBlame = new LinkedList<InputFile>();
-    for (InputFile f : inputPathCache.allFiles()) {
+    for (InputFile f : fs.inputFiles(fs.predicates().all())) {
       if (configuration.forceReloadAll()) {
         addIfNotEmpty(filesToBlame, (DefaultInputFile) f);
       } else {
@@ -110,8 +107,7 @@ public final class ScmSensor implements Sensor {
   }
 
   private void addIfNotEmpty(List<InputFile> filesToBlame, DefaultInputFile f) {
-    InputFileMetadata metadata = inputPathCache.getFileMetadata(f);
-    if (!metadata.isEmpty()) {
+    if (!f.isEmpty()) {
       filesToBlame.add(f);
     }
   }
index cf2930a72fe1a20c60764d9f8a40878a1913fa09..724c900176e7d3f304c2ec9ecfef148136cf7b42 100644 (file)
  */
 package org.sonar.batch.sensor;
 
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputDir;
+import com.google.common.collect.Iterables;
+import org.sonar.api.batch.fs.*;
 import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputPath;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.measure.MetricFinder;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.sensor.duplication.Duplication;
 import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
+import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
 import org.sonar.api.batch.sensor.internal.SensorStorage;
 import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.issue.Issue.Severity;
 import org.sonar.api.batch.sensor.measure.Measure;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
-import org.sonar.api.component.ResourcePerspectives;
 import org.sonar.api.config.Settings;
 import org.sonar.api.design.Dependency;
-import org.sonar.api.issue.Issuable;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.measures.Formula;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.measures.PersistenceMode;
 import org.sonar.api.measures.SumChildDistributionFormula;
-import org.sonar.api.resources.Directory;
-import org.sonar.api.resources.File;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.resources.Scopes;
+import org.sonar.api.resources.*;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.source.Symbol;
 import org.sonar.batch.duplication.DuplicationCache;
-import org.sonar.batch.highlighting.SyntaxHighlightingData;
 import org.sonar.batch.index.BatchResource;
-import org.sonar.batch.index.ComponentDataCache;
 import org.sonar.batch.index.DefaultIndex;
 import org.sonar.batch.index.ResourceCache;
+import org.sonar.batch.issue.ModuleIssues;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.protocol.output.BatchReport.Range;
+import org.sonar.batch.protocol.output.BatchReport.SyntaxHighlighting.HighlightingRule;
+import org.sonar.batch.protocol.output.BatchReportWriter;
+import org.sonar.batch.report.BatchReportUtils;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.sensor.coverage.CoverageExclusions;
+import org.sonar.batch.source.DefaultSymbol;
 import org.sonar.core.component.ComponentKeys;
-import org.sonar.core.source.SnapshotDataTypes;
+
+import java.util.Map;
+import java.util.Set;
 
 public class DefaultSensorStorage implements SensorStorage {
 
   private static final String USES = "USES";
   private final MetricFinder metricFinder;
   private final Project project;
-  private final ResourcePerspectives perspectives;
+  private final ModuleIssues moduleIssues;
   private final DefaultIndex sonarIndex;
   private final CoverageExclusions coverageExclusions;
   private final DuplicationCache duplicationCache;
   private final ResourceCache resourceCache;
-  private final ComponentDataCache componentDataCache;
+  private final ReportPublisher reportPublisher;
 
-  public DefaultSensorStorage(MetricFinder metricFinder, Project project,
-    ResourcePerspectives perspectives,
-    Settings settings, FileSystem fs, ActiveRules activeRules, ComponentDataCache componentDataCache,
-    DuplicationCache duplicationCache, DefaultIndex sonarIndex, CoverageExclusions coverageExclusions,
-    ResourceCache resourceCache) {
+  public DefaultSensorStorage(MetricFinder metricFinder, Project project, ModuleIssues moduleIssues,
+    Settings settings, FileSystem fs, ActiveRules activeRules, DuplicationCache duplicationCache, DefaultIndex sonarIndex,
+    CoverageExclusions coverageExclusions, ResourceCache resourceCache, ReportPublisher reportPublisher) {
     this.metricFinder = metricFinder;
     this.project = project;
-    this.perspectives = perspectives;
-    this.componentDataCache = componentDataCache;
+    this.moduleIssues = moduleIssues;
     this.sonarIndex = sonarIndex;
     this.coverageExclusions = coverageExclusions;
     this.duplicationCache = duplicationCache;
     this.resourceCache = resourceCache;
+    this.reportPublisher = reportPublisher;
   }
 
   private Metric findMetricOrFail(String metricKey) {
@@ -161,11 +162,7 @@ public class DefaultSensorStorage implements SensorStorage {
     } else {
       r = project;
     }
-    Issuable issuable = perspectives.as(Issuable.class, r);
-    if (issuable == null) {
-      return;
-    }
-    issuable.addIssue(toDefaultIssue(project.getKey(), ComponentKeys.createEffectiveKey(project, r), issue));
+    moduleIssues.initAndAddIssue(toDefaultIssue(project.getKey(), ComponentKeys.createEffectiveKey(project, r), issue));
   }
 
   public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) {
@@ -246,7 +243,56 @@ public class DefaultSensorStorage implements SensorStorage {
 
   @Override
   public void store(DefaultHighlighting highlighting) {
-    String componentKey = ((DefaultInputFile) highlighting.inputFile()).key();
-    componentDataCache.setData(componentKey, SnapshotDataTypes.SYNTAX_HIGHLIGHTING, new SyntaxHighlightingData(highlighting.getSyntaxHighlightingRuleSet()));
+    BatchReportWriter writer = reportPublisher.getWriter();
+    DefaultInputFile inputFile = (DefaultInputFile) highlighting.inputFile();
+    writer.writeComponentSyntaxHighlighting(resourceCache.get(inputFile).batchId(),
+      Iterables.transform(highlighting.getSyntaxHighlightingRuleSet(), new Function<SyntaxHighlightingRule, HighlightingRule>() {
+        private HighlightingRule.Builder builder = HighlightingRule.newBuilder();
+        private Range.Builder rangeBuilder = Range.newBuilder();
+
+        @Override
+        public HighlightingRule apply(SyntaxHighlightingRule input) {
+          builder.clear();
+          rangeBuilder.clear();
+          builder.setRange(rangeBuilder.setStartLine(input.range().start().line())
+            .setStartOffset(input.range().start().lineOffset())
+            .setEndLine(input.range().end().line())
+            .setEndOffset(input.range().end().lineOffset())
+            .build());
+          builder.setType(BatchReportUtils.toProtocolType(input.getTextType()));
+          return builder.build();
+        }
+
+      }));
+  }
+
+  public void store(DefaultInputFile inputFile, Map<Symbol, Set<TextRange>> referencesBySymbol) {
+    BatchReportWriter writer = reportPublisher.getWriter();
+    writer.writeComponentSymbols(resourceCache.get(inputFile).batchId(),
+      Iterables.transform(referencesBySymbol.entrySet(), new Function<Map.Entry<Symbol, Set<TextRange>>, BatchReport.Symbols.Symbol>() {
+        private BatchReport.Symbols.Symbol.Builder builder = BatchReport.Symbols.Symbol.newBuilder();
+        private Range.Builder rangeBuilder = Range.newBuilder();
+
+        @Override
+        public BatchReport.Symbols.Symbol apply(Map.Entry<Symbol, Set<TextRange>> input) {
+          builder.clear();
+          rangeBuilder.clear();
+          DefaultSymbol symbol = (DefaultSymbol) input.getKey();
+          builder.setDeclaration(rangeBuilder.setStartLine(symbol.range().start().line())
+            .setStartOffset(symbol.range().start().lineOffset())
+            .setEndLine(symbol.range().end().line())
+            .setEndOffset(symbol.range().end().lineOffset())
+            .build());
+          for (TextRange reference : input.getValue()) {
+            builder.addReference(rangeBuilder.setStartLine(reference.start().line())
+              .setStartOffset(reference.start().lineOffset())
+              .setEndLine(reference.end().line())
+              .setEndOffset(reference.end().lineOffset())
+              .build());
+          }
+          return builder.build();
+        }
+
+      }));
   }
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizerSensor.java b/sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizerSensor.java
new file mode 100644 (file)
index 0000000..ea23143
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.batch.source;
+
+import org.sonar.api.batch.Phase;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.batch.index.ResourceCache;
+import org.sonar.batch.protocol.output.BatchReport.SyntaxHighlighting.HighlightingRule;
+import org.sonar.batch.protocol.output.BatchReportReader;
+import org.sonar.batch.report.ReportPublisher;
+import org.sonar.colorizer.CodeColorizer;
+
+import java.util.List;
+
+/**
+ * Execute deprecated {@link CodeColorizer} if necessary.
+ */
+@Phase(name = Phase.Name.POST)
+public final class CodeColorizerSensor implements Sensor {
+
+  private final ReportPublisher reportPublisher;
+  private final ResourceCache resourceCache;
+  private final CodeColorizers codeColorizers;
+
+  public CodeColorizerSensor(ReportPublisher reportPublisher, ResourceCache resourceCache, CodeColorizers codeColorizers) {
+    this.reportPublisher = reportPublisher;
+    this.resourceCache = resourceCache;
+    this.codeColorizers = codeColorizers;
+  }
+
+  @Override
+  public void describe(SensorDescriptor descriptor) {
+    descriptor.name("Code Colorizer Sensor")
+      .disabledInPreview();
+  }
+
+  @Override
+  public void execute(final SensorContext context) {
+    FileSystem fs = context.fileSystem();
+    for (InputFile f : fs.inputFiles(fs.predicates().all())) {
+      BatchReportReader reader = new BatchReportReader(reportPublisher.getReportDir());
+      int batchId = resourceCache.get(f).batchId();
+      List<HighlightingRule> highlightingRules = reader.readComponentSyntaxHighlighting(batchId);
+      String language = f.language();
+      if (!highlightingRules.isEmpty() || language == null) {
+        continue;
+      }
+      codeColorizers.toSyntaxHighlighting(f.file(), fs.encoding(), language, context.newHighlighting().onFile(f));
+    }
+  }
+
+}
index 4b99191538a49a318edb52450a54d62c31fba1e3..9f38c69c63fffb03583520fafc5c17b0cc645896 100644 (file)
@@ -25,18 +25,14 @@ import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
 import org.sonar.api.web.CodeColorizerFormat;
-import org.sonar.batch.highlighting.SyntaxHighlightingData;
 import org.sonar.colorizer.CodeColorizer;
 import org.sonar.colorizer.Tokenizer;
 
 import javax.annotation.CheckForNull;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
+import java.io.*;
 import java.nio.charset.Charset;
 import java.util.HashMap;
 import java.util.List;
@@ -68,7 +64,7 @@ public class CodeColorizers implements BatchComponent {
   }
 
   @CheckForNull
-  public SyntaxHighlightingData toSyntaxHighlighting(File file, Charset charset, String language) {
+  public void toSyntaxHighlighting(File file, Charset charset, String language, NewHighlighting highlighting) {
     CodeColorizerFormat format = byLang.get(language);
     List<Tokenizer> tokenizers;
     if (format == null) {
@@ -77,13 +73,13 @@ public class CodeColorizers implements BatchComponent {
       if ("java".equals(language)) {
         tokenizers = CodeColorizer.Format.JAVA.getTokenizers();
       } else {
-        return null;
+        return;
       }
     } else {
       tokenizers = format.getTokenizers();
     }
     try (Reader reader = new BufferedReader(new InputStreamReader(new BOMInputStream(new FileInputStream(file)), charset))) {
-      return new HighlightingRenderer().render(reader, tokenizers);
+      new HighlightingRenderer().render(reader, tokenizers, highlighting);
     } catch (Exception e) {
       throw new IllegalStateException("Unable to read source file for colorization", e);
     }
index bef343b872a89dd81c0b2006842dcbd447a62e64..741346dd99bafeee40677961ecf51bf27ed2107b 100644 (file)
  */
 package org.sonar.batch.source;
 
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.highlighting.TypeOfText;
+import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
 import org.sonar.api.component.Component;
 import org.sonar.api.source.Highlightable;
-import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder;
-import org.sonar.batch.index.ComponentDataCache;
-import org.sonar.core.source.SnapshotDataTypes;
+import org.sonar.batch.deprecated.InputFileComponent;
 
 /**
  * @since 3.6
  */
 public class DefaultHighlightable implements Highlightable {
 
-  private final Component component;
-  private final ComponentDataCache cache;
-  private final SyntaxHighlightingDataBuilder builder;
+  private final DefaultInputFile inputFile;
+  private final SensorStorage sensorStorage;
 
-  public DefaultHighlightable(Component component, ComponentDataCache cache) {
-    this.component = component;
-    this.cache = cache;
-    this.builder = new SyntaxHighlightingDataBuilder();
+  public DefaultHighlightable(DefaultInputFile inputFile, SensorStorage sensorStorage) {
+    this.inputFile = inputFile;
+    this.sensorStorage = sensorStorage;
   }
 
   @Override
   public HighlightingBuilder newHighlighting() {
-    return new DefaultHighlightingBuilder(component.key(), cache, builder);
+    DefaultHighlighting defaultHighlighting = new DefaultHighlighting(sensorStorage);
+    defaultHighlighting.onFile(inputFile);
+    return new DefaultHighlightingBuilder(defaultHighlighting);
   }
 
   @Override
   public Component component() {
-    return component;
-  }
-
-  public SyntaxHighlightingDataBuilder getHighlightingRules() {
-    return builder;
+    return new InputFileComponent(inputFile);
   }
 
   private static class DefaultHighlightingBuilder implements HighlightingBuilder {
 
-    private final SyntaxHighlightingDataBuilder builder;
-    private String componentKey;
-    private ComponentDataCache cache;
+    private final DefaultHighlighting defaultHighlighting;
 
-    public DefaultHighlightingBuilder(String componentKey, ComponentDataCache cache, SyntaxHighlightingDataBuilder builder) {
-      this.componentKey = componentKey;
-      this.cache = cache;
-      this.builder = builder;
+    public DefaultHighlightingBuilder(DefaultHighlighting defaultHighlighting) {
+      this.defaultHighlighting = defaultHighlighting;
     }
 
     @Override
     public HighlightingBuilder highlight(int startOffset, int endOffset, String typeOfText) {
       TypeOfText type = org.sonar.api.batch.sensor.highlighting.TypeOfText.forCssClass(typeOfText);
-      builder.registerHighlightingRule(startOffset, endOffset, type);
+      defaultHighlighting.highlight(startOffset, endOffset, type);
       return this;
     }
 
     @Override
     public void done() {
-      cache.setData(componentKey, SnapshotDataTypes.SYNTAX_HIGHLIGHTING, builder.build());
+      defaultHighlighting.save();
     }
   }
 }
index 0bca8bd999719d02e490ecd7a8228bea81a8b398..2ca34b67a9a0655477a7d8ace7981876a55edd0a 100644 (file)
 package org.sonar.batch.source;
 
 import com.google.common.base.Objects;
+import org.sonar.api.batch.fs.TextRange;
 
 import java.io.Serializable;
 
 public class DefaultSymbol implements org.sonar.api.source.Symbol, Serializable {
 
-  private final int declarationStartOffset;
-  private final int declarationEndOffset;
+  private TextRange range;
+  private int length;
 
-  public DefaultSymbol(int startOffset, int endOffset) {
-    this.declarationStartOffset = startOffset;
-    this.declarationEndOffset = endOffset;
+  public DefaultSymbol(TextRange range, int length) {
+    this.range = range;
+    this.length = length;
   }
 
   @Override
   public int getDeclarationStartOffset() {
-    return declarationStartOffset;
+    throw new UnsupportedOperationException("getDeclarationStartOffset");
   }
 
   @Override
   public int getDeclarationEndOffset() {
-    return declarationEndOffset;
+    throw new UnsupportedOperationException("getDeclarationEndOffset");
   }
 
   @Override
   public String getFullyQualifiedName() {
-    return null;
+    throw new UnsupportedOperationException("getFullyQualifiedName");
+  }
+
+  public TextRange range() {
+    return range;
+  }
+
+  public int getLength() {
+    return length;
   }
 
   @Override
   public String toString() {
     return Objects.toStringHelper("Symbol")
-      .add("offset", String.format("%d-%d", declarationStartOffset, declarationEndOffset))
+      .add("range", range)
       .toString();
   }
 }
index 18b3e06df6b6ec6d0296279912042983f0392981..2ab9999bf1be96b51433c6fefbf2dd6aec1d7e7c 100644 (file)
 
 package org.sonar.batch.source;
 
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.source.Symbol;
 import org.sonar.api.source.Symbolizable;
 
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.*;
 
 public class DefaultSymbolTable implements Symbolizable.SymbolTable {
 
-  private Map<Symbol, Set<Integer>> referencesBySymbol;
+  private Map<Symbol, Set<TextRange>> referencesBySymbol;
 
-  private DefaultSymbolTable(Map<Symbol, Set<Integer>> referencesBySymbol) {
+  private DefaultSymbolTable(Map<Symbol, Set<TextRange>> referencesBySymbol) {
     this.referencesBySymbol = referencesBySymbol;
   }
 
-  public Map<Symbol, Set<Integer>> getReferencesBySymbol() {
+  public Map<Symbol, Set<TextRange>> getReferencesBySymbol() {
     return referencesBySymbol;
   }
 
@@ -53,22 +50,28 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable {
 
   @Override
   public List<Integer> references(Symbol symbol) {
-    return new ArrayList<Integer>(referencesBySymbol.get(symbol));
+    throw new UnsupportedOperationException("references");
   }
 
   public static class Builder implements Symbolizable.SymbolTableBuilder {
 
-    private final Map<Symbol, Set<Integer>> referencesBySymbol = new LinkedHashMap<Symbol, Set<Integer>>();
-    private final String componentKey;
+    private final Map<Symbol, Set<TextRange>> referencesBySymbol = new LinkedHashMap<Symbol, Set<TextRange>>();
+    private final DefaultInputFile inputFile;
 
-    public Builder(String componentKey) {
-      this.componentKey = componentKey;
+    public Builder(DefaultInputFile inputFile) {
+      this.inputFile = inputFile;
     }
 
     @Override
     public Symbol newSymbol(int fromOffset, int toOffset) {
-      Symbol symbol = new DefaultSymbol(fromOffset, toOffset);
-      referencesBySymbol.put(symbol, new TreeSet<Integer>());
+      TextRange declarationRange = inputFile.newRange(fromOffset, toOffset);
+      DefaultSymbol symbol = new DefaultSymbol(declarationRange, toOffset - fromOffset);
+      referencesBySymbol.put(symbol, new TreeSet<TextRange>(new Comparator<TextRange>() {
+        @Override
+        public int compare(TextRange o1, TextRange o2) {
+          return o1.start().compareTo(o2.start());
+        }
+      }));
       return symbol;
     }
 
@@ -77,10 +80,12 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable {
       if (!referencesBySymbol.containsKey(symbol)) {
         throw new UnsupportedOperationException("Cannot add reference to a symbol in another file");
       }
-      if (fromOffset >= symbol.getDeclarationStartOffset() && fromOffset < symbol.getDeclarationEndOffset()) {
-        throw new UnsupportedOperationException("Cannot add reference (" + fromOffset + ") overlapping " + symbol + " in " + componentKey);
+      TextRange referenceRange = inputFile.newRange(fromOffset, fromOffset + ((DefaultSymbol) symbol).getLength());
+
+      if (referenceRange.overlap(((DefaultSymbol) symbol).range())) {
+        throw new UnsupportedOperationException("Cannot add reference (" + fromOffset + ") overlapping " + symbol + " in " + inputFile.key());
       }
-      referencesBySymbol.get(symbol).add(fromOffset);
+      referencesBySymbol.get(symbol).add(referenceRange);
     }
 
     @Override
index 8a6f6421408439e31d321ef9f0e2f501b6fb2381..037913f06cd8b0e0d91ff053cc88bf599d23ff4e 100644 (file)
 
 package org.sonar.batch.source;
 
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.component.Component;
 import org.sonar.api.source.Symbolizable;
-import org.sonar.batch.index.ComponentDataCache;
-import org.sonar.batch.symbol.SymbolData;
-import org.sonar.core.source.SnapshotDataTypes;
+import org.sonar.batch.deprecated.InputFileComponent;
+import org.sonar.batch.sensor.DefaultSensorStorage;
 
 public class DefaultSymbolizable implements Symbolizable {
 
-  private final ComponentDataCache cache;
-  private final Component component;
+  private final DefaultInputFile inputFile;
+  private final DefaultSensorStorage sensorStorage;
 
-  public DefaultSymbolizable(ComponentDataCache cache, Component component) {
-    this.cache = cache;
-    this.component = component;
+  public DefaultSymbolizable(DefaultInputFile inputFile, DefaultSensorStorage sensorStorage) {
+    this.inputFile = inputFile;
+    this.sensorStorage = sensorStorage;
   }
 
   @Override
   public Component component() {
-    return component;
+    return new InputFileComponent(inputFile);
   }
 
   @Override
   public SymbolTableBuilder newSymbolTableBuilder() {
-    return new DefaultSymbolTable.Builder(component.key());
+    return new DefaultSymbolTable.Builder(inputFile);
   }
 
   @Override
   public void setSymbolTable(SymbolTable symbolTable) {
-    SymbolData symbolData = new SymbolData(((DefaultSymbolTable) symbolTable).getReferencesBySymbol());
-    cache.setData(component().key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING, symbolData);
+    sensorStorage.store(inputFile, ((DefaultSymbolTable) symbolTable).getReferencesBySymbol());
   }
 }
index d20fbebb14712442be782953335af9872e175dbd..fa9326a160485b846919b042d689629bfca06a9a 100644 (file)
 package org.sonar.batch.source;
 
 import com.google.common.collect.ImmutableSet;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
 import org.sonar.api.component.Component;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.source.Highlightable;
-import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.batch.index.BatchResource;
+import org.sonar.batch.index.ResourceCache;
 import org.sonar.core.component.PerspectiveBuilder;
 import org.sonar.core.component.ResourceComponent;
 
@@ -31,27 +35,30 @@ import javax.annotation.CheckForNull;
 
 import java.util.Set;
 
-/**
- * @since 3.6
- * @deprecated since 4.5 no more used in batch 2.0
- */
-@Deprecated
 public class HighlightableBuilder extends PerspectiveBuilder<Highlightable> {
 
   private static final Set<String> SUPPORTED_QUALIFIERS = ImmutableSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE);
-  private final ComponentDataCache cache;
+  private final ResourceCache cache;
+  private final SensorStorage sensorStorage;
 
-  public HighlightableBuilder(ComponentDataCache cache) {
+  public HighlightableBuilder(ResourceCache cache, SensorStorage sensorStorage) {
     super(Highlightable.class);
     this.cache = cache;
+    this.sensorStorage = sensorStorage;
   }
 
   @CheckForNull
   @Override
-  protected Highlightable loadPerspective(Class<Highlightable> perspectiveClass, Component component) {
+  public Highlightable loadPerspective(Class<Highlightable> perspectiveClass, Component component) {
     boolean supported = SUPPORTED_QUALIFIERS.contains(component.qualifier());
     if (supported && component instanceof ResourceComponent) {
-      return new DefaultHighlightable(component, cache);
+      BatchResource batchComponent = cache.get(component.key());
+      if (batchComponent != null) {
+        InputFile path = (InputFile) batchComponent.inputPath();
+        if (path != null) {
+          return new DefaultHighlightable((DefaultInputFile) path, sensorStorage);
+        }
+      }
     }
     return null;
   }
index 8da5515fdaa4c9bf1a69a9ce2a3f7958e334f5c2..687f2fecc2da4032bbac063eb79f9d3488433f8b 100644 (file)
@@ -21,9 +21,8 @@ package org.sonar.batch.source;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
 import org.sonar.api.batch.sensor.highlighting.TypeOfText;
-import org.sonar.batch.highlighting.SyntaxHighlightingData;
-import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder;
 import org.sonar.colorizer.HtmlCodeBuilder;
 
 import java.util.regex.Matcher;
@@ -33,12 +32,16 @@ public class HighlightingCodeBuilder extends HtmlCodeBuilder {
 
   private static final Logger LOG = LoggerFactory.getLogger(HighlightingCodeBuilder.class);
 
-  private SyntaxHighlightingDataBuilder highlightingBuilder = new SyntaxHighlightingDataBuilder();
   private int currentOffset = 0;
   private static final Pattern START_TAG_PATTERN = Pattern.compile("<span class=\"(.+)\">");
   private static final Pattern END_TAG_PATTERN = Pattern.compile("</span>");
   private int startOffset = -1;
   private String cssClass;
+  private final NewHighlighting highlighting;
+
+  public HighlightingCodeBuilder(NewHighlighting highlighting) {
+    this.highlighting = highlighting;
+  }
 
   @Override
   public Appendable append(CharSequence csq) {
@@ -67,7 +70,7 @@ public class HighlightingCodeBuilder extends HtmlCodeBuilder {
     } else {
       Matcher endMatcher = END_TAG_PATTERN.matcher(htmlTag);
       if (endMatcher.matches()) {
-        highlightingBuilder.registerHighlightingRule(startOffset, currentOffset, TypeOfText.forCssClass(cssClass));
+        highlighting.highlight(startOffset, currentOffset, TypeOfText.forCssClass(cssClass));
         startOffset = -1;
       } else {
         LOG.warn("Expected to match highlighting end html tag but was: " + htmlTag);
@@ -85,8 +88,4 @@ public class HighlightingCodeBuilder extends HtmlCodeBuilder {
     throw new UnsupportedOperationException();
   }
 
-  public SyntaxHighlightingData getHighlightingData() {
-    return highlightingBuilder.build();
-  }
-
 }
index e2a5faa592e747ef87360b70448fabdd8702b7ac..6858d6de1f02344c41c3d02cc012b9bf5eef27dc 100644 (file)
@@ -19,7 +19,7 @@
  */
 package org.sonar.batch.source;
 
-import org.sonar.batch.highlighting.SyntaxHighlightingData;
+import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
 import org.sonar.channel.Channel;
 import org.sonar.channel.CodeReader;
 import org.sonar.colorizer.HtmlCodeBuilder;
@@ -31,14 +31,13 @@ import java.util.List;
 
 public class HighlightingRenderer {
 
-  public SyntaxHighlightingData render(Reader code, List<? extends Channel<HtmlCodeBuilder>> tokenizers) {
+  public void render(Reader code, List<? extends Channel<HtmlCodeBuilder>> tokenizers, NewHighlighting highlighting) {
     List<Channel<HtmlCodeBuilder>> allTokenizers = new ArrayList<Channel<HtmlCodeBuilder>>();
-    HighlightingCodeBuilder codeBuilder = new HighlightingCodeBuilder();
+    HighlightingCodeBuilder codeBuilder = new HighlightingCodeBuilder(highlighting);
 
     allTokenizers.addAll(tokenizers);
 
     new TokenizerDispatcher(allTokenizers).colorize(new CodeReader(code), codeBuilder);
-
-    return codeBuilder.getHighlightingData();
+    highlighting.save();
   }
 }
index 5851824f169e3b269c746764cb3b33307ebe8958..a38d5c2df4faa191c7ebe1ce41a4e27d803fd672 100644 (file)
@@ -29,18 +29,10 @@ import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.batch.sensor.SensorDescriptor;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.measures.CoreMetrics;
-import org.sonar.batch.scan.filesystem.InputFileMetadata;
-import org.sonar.batch.scan.filesystem.InputPathCache;
 
 @Phase(name = Phase.Name.PRE)
 public final class LinesSensor implements Sensor {
 
-  private final InputPathCache inputPathCache;
-
-  public LinesSensor(InputPathCache inputPathCache) {
-    this.inputPathCache = inputPathCache;
-  }
-
   @Override
   public void describe(SensorDescriptor descriptor) {
     descriptor.name("Lines Sensor");
@@ -58,11 +50,10 @@ public final class LinesSensor implements Sensor {
         .save();
       if (f.language() == null) {
         // As an approximation for files with no language plugin we consider every non blank line as ncloc
-        InputFileMetadata metadata = inputPathCache.getFileMetadata((DefaultInputFile) f);
         ((DefaultMeasure<Integer>) context.<Integer>newMeasure()
           .onFile(f)
           .forMetric(CoreMetrics.NCLOC)
-          .withValue(metadata.nonBlankLines()))
+          .withValue(((DefaultInputFile) f).nonBlankLines()))
           .save();
         // No test and no coverage on those files
         ((DefaultMeasure<Integer>) context.<Integer>newMeasure()
index 915dc285fa6ec40518a142bc601506211e89ef60..212c31d87b41dade737bc5b17608671113bd1694 100644 (file)
 
 package org.sonar.batch.source;
 
+import com.google.common.collect.ImmutableSet;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.component.Component;
+import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.source.Symbolizable;
-import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.batch.index.BatchResource;
+import org.sonar.batch.index.ResourceCache;
+import org.sonar.batch.sensor.DefaultSensorStorage;
 import org.sonar.core.component.PerspectiveBuilder;
+import org.sonar.core.component.ResourceComponent;
+
+import javax.annotation.CheckForNull;
+
+import java.util.Set;
 
 public class SymbolizableBuilder extends PerspectiveBuilder<Symbolizable> {
 
-  private final ComponentDataCache cache;
+  private static final Set<String> SUPPORTED_QUALIFIERS = ImmutableSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE);
+  private final ResourceCache cache;
+  private final DefaultSensorStorage sensorStorage;
 
-  public SymbolizableBuilder(ComponentDataCache cache) {
+  public SymbolizableBuilder(ResourceCache cache, DefaultSensorStorage sensorStorage) {
     super(Symbolizable.class);
     this.cache = cache;
+    this.sensorStorage = sensorStorage;
   }
 
+  @CheckForNull
   @Override
-  protected Symbolizable loadPerspective(Class<Symbolizable> perspectiveClass, Component component) {
-    return new DefaultSymbolizable(cache, component);
+  public Symbolizable loadPerspective(Class<Symbolizable> perspectiveClass, Component component) {
+    boolean supported = SUPPORTED_QUALIFIERS.contains(component.qualifier());
+    if (supported && component instanceof ResourceComponent) {
+      BatchResource batchComponent = cache.get(component.key());
+      if (batchComponent != null) {
+        InputFile path = (InputFile) batchComponent.inputPath();
+        if (path != null) {
+          return new DefaultSymbolizable((DefaultInputFile) path, sensorStorage);
+        }
+      }
+    }
+    return null;
   }
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java
deleted file mode 100644 (file)
index fb7b92d..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.batch.symbol;
-
-import org.sonar.api.source.Symbol;
-import org.sonar.batch.index.ComponentDataCache;
-import org.sonar.batch.source.DefaultSymbol;
-import org.sonar.core.source.SnapshotDataTypes;
-
-import java.io.Serializable;
-import java.util.Comparator;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-public class DefaultSymbolTableBuilder {
-
-  private final String componentKey;
-  private final ComponentDataCache cache;
-  private final Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol = new LinkedHashMap<org.sonar.api.source.Symbol, Set<Integer>>();
-
-  public DefaultSymbolTableBuilder(String componentKey, ComponentDataCache cache) {
-    this.componentKey = componentKey;
-    this.cache = cache;
-  }
-
-  public Symbol newSymbol(int fromOffset, int toOffset) {
-    org.sonar.api.source.Symbol symbol = new DefaultSymbol(fromOffset, toOffset);
-    referencesBySymbol.put(symbol, new TreeSet<Integer>());
-    return symbol;
-  }
-
-  public void newReference(Symbol symbol, int fromOffset) {
-    if (!referencesBySymbol.containsKey(symbol)) {
-      throw new UnsupportedOperationException("Cannot add reference to a symbol in another file");
-    }
-    if (fromOffset >= symbol.getDeclarationStartOffset() && fromOffset < symbol.getDeclarationEndOffset()) {
-      throw new UnsupportedOperationException("Cannot add reference (" + fromOffset + ") overlapping " + symbol);
-    }
-    referencesBySymbol.get(symbol).add(fromOffset);
-  }
-
-  public SymbolData build() {
-    return new SymbolData(referencesBySymbol);
-  }
-
-  public void done() {
-    cache.setData(componentKey, SnapshotDataTypes.SYMBOL_HIGHLIGHTING, build());
-  }
-
-  public static class SymbolComparator implements Comparator<Symbol>, Serializable {
-    @Override
-    public int compare(Symbol left, Symbol right) {
-      return left.getDeclarationStartOffset() - right.getDeclarationStartOffset();
-    }
-  }
-
-  public static class ReferenceComparator implements Comparator<Integer>, Serializable {
-    @Override
-    public int compare(Integer left, Integer right) {
-      int result;
-      if (left != null && right != null) {
-        result = left - right;
-      } else {
-        result = left == null ? -1 : 1;
-      }
-      return result;
-    }
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java b/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java
deleted file mode 100644 (file)
index 6164292..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.batch.symbol;
-
-import org.sonar.api.source.Symbol;
-import org.sonar.batch.index.Data;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-
-public class SymbolData implements Data {
-
-  public static final String FIELD_SEPARATOR = ",";
-  public static final String SYMBOL_SEPARATOR = ";";
-
-  private final Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol;
-
-  public SymbolData(Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol) {
-    this.referencesBySymbol = referencesBySymbol;
-  }
-
-  public Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol() {
-    return referencesBySymbol;
-  }
-
-  @Override
-  public String writeString() {
-    StringBuilder sb = new StringBuilder();
-
-    for (Symbol symbol : referencesBySymbol.keySet()) {
-      if (sb.length() > 0) {
-        sb.append(SYMBOL_SEPARATOR);
-      }
-
-      sb.append(symbol.getDeclarationStartOffset())
-        .append(FIELD_SEPARATOR)
-        .append(symbol.getDeclarationEndOffset())
-        .append(FIELD_SEPARATOR)
-        .append(symbol.getDeclarationStartOffset());
-      Collection<Integer> symbolReferences = referencesBySymbol.get(symbol);
-      for (Integer symbolReference : symbolReferences) {
-        sb.append(FIELD_SEPARATOR).append(symbolReference);
-      }
-    }
-
-    return sb.toString();
-  }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/symbol/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/symbol/package-info.java
deleted file mode 100644 (file)
index 2ac5be1..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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.
- */
-@javax.annotation.ParametersAreNonnullByDefault
-package org.sonar.batch.symbol;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilderTest.java
deleted file mode 100644 (file)
index b4c4828..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.batch.highlighting;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
-
-import java.util.Collection;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.api.batch.sensor.highlighting.TypeOfText.COMMENT;
-import static org.sonar.api.batch.sensor.highlighting.TypeOfText.CPP_DOC;
-import static org.sonar.api.batch.sensor.highlighting.TypeOfText.KEYWORD;
-
-public class SyntaxHighlightingDataBuilderTest {
-
-  private Collection<SyntaxHighlightingRule> highlightingRules;
-
-  @Rule
-  public ExpectedException throwable = ExpectedException.none();
-
-  @Before
-  public void setUpSampleRules() {
-
-    SyntaxHighlightingDataBuilder highlightingDataBuilder = new SyntaxHighlightingDataBuilder();
-    highlightingDataBuilder.registerHighlightingRule(0, 10, COMMENT);
-    highlightingDataBuilder.registerHighlightingRule(10, 12, KEYWORD);
-    highlightingDataBuilder.registerHighlightingRule(24, 38, KEYWORD);
-    highlightingDataBuilder.registerHighlightingRule(42, 50, KEYWORD);
-    highlightingDataBuilder.registerHighlightingRule(24, 65, CPP_DOC);
-    highlightingDataBuilder.registerHighlightingRule(12, 20, COMMENT);
-
-    highlightingRules = highlightingDataBuilder.getSyntaxHighlightingRuleSet();
-  }
-
-  @Test
-  public void should_register_highlighting_rule() throws Exception {
-    assertThat(highlightingRules).hasSize(6);
-  }
-
-  @Test
-  public void should_order_by_start_then_end_offset() throws Exception {
-    assertThat(highlightingRules).extracting("startPosition").containsOnly(0, 10, 12, 24, 24, 42);
-    assertThat(highlightingRules).extracting("endPosition").containsOnly(10, 12, 20, 38, 65, 50);
-    assertThat(highlightingRules).extracting("textType").containsOnly(COMMENT, KEYWORD, COMMENT, KEYWORD, CPP_DOC, KEYWORD);
-  }
-
-  @Test
-  public void should_suport_overlapping() throws Exception {
-    SyntaxHighlightingDataBuilder builder = new SyntaxHighlightingDataBuilder();
-    builder.registerHighlightingRule(0, 15, KEYWORD);
-    builder.registerHighlightingRule(8, 12, CPP_DOC);
-    builder.build();
-  }
-
-  @Test
-  public void should_prevent_boudaries_overlapping() throws Exception {
-    throwable.expect(IllegalStateException.class);
-    throwable.expectMessage("Cannot register highlighting rule for characters from 8 to 15 as it overlaps at least one existing rule");
-
-    SyntaxHighlightingDataBuilder builder = new SyntaxHighlightingDataBuilder();
-    builder.registerHighlightingRule(0, 10, KEYWORD);
-    builder.registerHighlightingRule(8, 15, KEYWORD);
-    builder.build();
-  }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataTest.java b/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataTest.java
deleted file mode 100644 (file)
index dcebfe2..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.batch.highlighting;
-
-import com.google.common.collect.Lists;
-import org.junit.Test;
-import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
-
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.api.batch.sensor.highlighting.TypeOfText.COMMENT;
-import static org.sonar.api.batch.sensor.highlighting.TypeOfText.CPP_DOC;
-import static org.sonar.api.batch.sensor.highlighting.TypeOfText.KEYWORD;
-
-public class SyntaxHighlightingDataTest {
-
-  @Test
-  public void should_serialize_rules_to_string() throws Exception {
-
-    List<SyntaxHighlightingRule> orderedHighlightingRules = Lists.newArrayList(
-      SyntaxHighlightingRule.create(0, 10, COMMENT),
-      SyntaxHighlightingRule.create(10, 12, KEYWORD),
-      SyntaxHighlightingRule.create(12, 20, COMMENT),
-      SyntaxHighlightingRule.create(24, 38, KEYWORD),
-      SyntaxHighlightingRule.create(24, 65, CPP_DOC),
-      SyntaxHighlightingRule.create(42, 50, KEYWORD)
-      );
-
-    String serializedRules = new SyntaxHighlightingData(orderedHighlightingRules).writeString();
-    assertThat(serializedRules).isEqualTo("0,10,cd;10,12,k;12,20,cd;24,38,k;24,65,cppd;42,50,k");
-  }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java
deleted file mode 100644 (file)
index 5fa9587..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.batch.index;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ComponentDataCacheTest {
-
-  @ClassRule
-  public static TemporaryFolder temp = new TemporaryFolder();
-
-  Caches caches;
-
-  @Before
-  public void start() throws Exception {
-    caches = CachesTest.createCacheOnTemp(temp);
-    caches.start();
-  }
-
-  @After
-  public void stop() {
-    caches.stop();
-  }
-
-  @Test
-  public void should_get_and_set_string_data() {
-    ComponentDataCache cache = new ComponentDataCache(caches);
-    cache.setStringData("org/struts/Action.java", "SYNTAX", "1:foo;3:bar");
-    assertThat(cache.getStringData("org/struts/Action.java", "SYNTAX")).isEqualTo("1:foo;3:bar");
-    assertThat(cache.getStringData("org/struts/Action.java", "OTHER")).isNull();
-    assertThat(cache.getStringData("Other.java", "SYNTAX")).isNull();
-    assertThat(cache.getStringData("Other.java", "OTHER")).isNull();
-  }
-
-  @Test
-  public void should_get_and_set_data() {
-    ComponentDataCache cache = new ComponentDataCache(caches);
-    cache.setData("org/struts/Action.java", "COUNT", new LongData(1234L));
-    LongData count = cache.getData("org/struts/Action.java", "COUNT");
-    assertThat(count.data()).isEqualTo(1234L);
-  }
-
-  static class LongData implements Data {
-
-    private long data;
-
-    LongData() {
-    }
-
-    LongData(long data) {
-      this.data = data;
-    }
-
-    public long data() {
-      return data;
-    }
-
-    @Override
-    public String writeString() {
-      return String.valueOf(data);
-    }
-
-  }
-}
index a37bef0da563e3098ae904b17fa478b6a8d167e1..50a2554aa3741bbed65f6b4e983b3ea88a392b10 100644 (file)
@@ -30,23 +30,18 @@ import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.duplication.Duplication;
 import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication;
-import org.sonar.api.batch.sensor.highlighting.TypeOfText;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.Metric;
-import org.sonar.api.source.Symbol;
 import org.sonar.batch.duplication.DuplicationCache;
-import org.sonar.batch.highlighting.SyntaxHighlightingData;
-import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder;
+import org.sonar.batch.protocol.Constants.HighlightingType;
+import org.sonar.batch.protocol.output.*;
+import org.sonar.batch.protocol.output.BatchReport.Range;
 import org.sonar.batch.protocol.output.BatchReport.Scm;
 import org.sonar.batch.protocol.output.BatchReport.Scm.Changeset;
-import org.sonar.batch.protocol.output.BatchReportWriter;
-import org.sonar.batch.report.PublishReportJob;
-import org.sonar.batch.scan.filesystem.InputFileMetadata;
+import org.sonar.batch.protocol.output.BatchReport.SyntaxHighlighting.HighlightingRule;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.scan.measure.MeasureCache;
-import org.sonar.batch.source.CodeColorizers;
-import org.sonar.batch.symbol.DefaultSymbolTableBuilder;
-import org.sonar.core.source.SnapshotDataTypes;
 import org.sonar.server.source.db.FileSourceDb;
 
 import java.io.File;
@@ -64,24 +59,23 @@ public class SourceDataFactoryTest {
   public TemporaryFolder temp = new TemporaryFolder();
 
   private MeasureCache measureCache = mock(MeasureCache.class);
-  private ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
   private DuplicationCache duplicationCache = mock(DuplicationCache.class);
-  private CodeColorizers colorizers = mock(CodeColorizers.class);
   private DefaultInputFile inputFile;
-  private InputFileMetadata metadata;
   private SourceDataFactory sut;
   private FileSourceDb.Data.Builder output;
   private File reportDir;
+  private BatchReportWriter batchReportWriter;
 
   @Before
   public void setUp() throws Exception {
-    PublishReportJob publishReportJob = mock(PublishReportJob.class);
+    ReportPublisher reportPublisher = mock(ReportPublisher.class);
     reportDir = temp.newFolder();
-    when(publishReportJob.getReportDir()).thenReturn(reportDir);
+    batchReportWriter = new BatchReportWriter(reportDir);
+    when(reportPublisher.getReportDir()).thenReturn(reportDir);
     ResourceCache resourceCache = new ResourceCache();
     resourceCache.add(org.sonar.api.resources.File.create("src/Foo.java").setEffectiveKey("module_key:src/Foo.java"), null);
     when(measureCache.byMetric(anyString(), anyString())).thenReturn(Collections.<Measure>emptyList());
-    sut = new SourceDataFactory(measureCache, componentDataCache, duplicationCache, colorizers, publishReportJob, resourceCache);
+    sut = new SourceDataFactory(measureCache, duplicationCache, reportPublisher, resourceCache);
     // generate a file with 3 lines
     File baseDir = temp.newFolder();
     DefaultFileSystem fs = new DefaultFileSystem(baseDir.toPath());
@@ -89,7 +83,6 @@ public class SourceDataFactoryTest {
       .setLines(3)
       .setCharset(Charsets.UTF_8);
     fs.add(inputFile);
-    metadata = new InputFileMetadata();
     FileUtils.write(inputFile.file(), "one\ntwo\nthree\n");
     output = sut.createForSource(inputFile);
     when(duplicationCache.byComponent(anyString())).thenReturn(Collections.<DefaultDuplication>emptyList());
@@ -106,7 +99,7 @@ public class SourceDataFactoryTest {
 
   @Test
   public void consolidateData() throws Exception {
-    byte[] bytes = sut.consolidateData(inputFile, metadata);
+    byte[] bytes = sut.consolidateData(inputFile);
     assertThat(bytes).isNotEmpty();
   }
 
@@ -247,9 +240,7 @@ public class SourceDataFactoryTest {
 
   @Test
   public void applyHighlighting_missing() throws Exception {
-    when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(null);
-
-    sut.applyHighlighting(inputFile, metadata, output);
+    sut.applyHighlighting(inputFile, output);
 
     FileSourceDb.Data data = output.build();
     assertThat(data.getLines(0).hasHighlighting()).isFalse();
@@ -259,49 +250,28 @@ public class SourceDataFactoryTest {
 
   @Test
   public void applyHighlighting() throws Exception {
-    SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder()
-      .registerHighlightingRule(0, 4, TypeOfText.ANNOTATION)
-      .registerHighlightingRule(4, 5, TypeOfText.COMMENT)
-      .registerHighlightingRule(7, 16, TypeOfText.CONSTANT)
-      .build();
-    when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting);
-    metadata.setOriginalLineOffsets(new int[] {0, 4, 7});
-
-    sut.applyHighlighting(inputFile, metadata, output);
+    batchReportWriter.writeComponentSyntaxHighlighting(1, Arrays.asList(
+      newRule(1, 0, 1, 4, HighlightingType.ANNOTATION),
+      newRule(2, 0, 2, 1, HighlightingType.COMMENT),
+      newRule(3, 1, 3, 9, HighlightingType.CONSTANT)));
+    inputFile.setOriginalLineOffsets(new int[] {0, 4, 7});
+    sut.applyHighlighting(inputFile, output);
 
     FileSourceDb.Data data = output.build();
     assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,4,a");
     assertThat(data.getLines(1).getHighlighting()).isEqualTo("0,1,cd");
-    assertThat(data.getLines(2).getHighlighting()).isEqualTo("0,9,c");
-  }
-
-  @Test
-  public void applyHighlighting_ignore_bad_line() throws Exception {
-    SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder()
-      .registerHighlightingRule(0, 4, TypeOfText.ANNOTATION)
-      .registerHighlightingRule(4, 5, TypeOfText.COMMENT)
-      .registerHighlightingRule(7, 25, TypeOfText.CONSTANT)
-      .build();
-    when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting);
-    metadata.setOriginalLineOffsets(new int[] {0, 4, 7, 15});
-
-    sut.applyHighlighting(inputFile, metadata, output);
-
-    FileSourceDb.Data data = output.build();
-    assertThat(data.getLinesCount()).isEqualTo(3);
+    assertThat(data.getLines(2).getHighlighting()).isEqualTo("1,9,c");
   }
 
   @Test
   public void applyHighlighting_multiple_lines() throws Exception {
-    SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder()
-      .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION)
-      .registerHighlightingRule(4, 9, TypeOfText.COMMENT)
-      .registerHighlightingRule(10, 16, TypeOfText.CONSTANT)
-      .build();
-    when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting);
-    metadata.setOriginalLineOffsets(new int[] {0, 4, 7});
+    batchReportWriter.writeComponentSyntaxHighlighting(1, Arrays.asList(
+      newRule(1, 0, 1, 3, HighlightingType.ANNOTATION),
+      newRule(2, 0, 3, 2, HighlightingType.COMMENT),
+      newRule(3, 3, 3, 9, HighlightingType.CONSTANT)));
+    inputFile.setOriginalLineOffsets(new int[] {0, 4, 7});
 
-    sut.applyHighlighting(inputFile, metadata, output);
+    sut.applyHighlighting(inputFile, output);
 
     FileSourceDb.Data data = output.build();
     assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,3,a");
@@ -311,16 +281,15 @@ public class SourceDataFactoryTest {
 
   @Test
   public void applyHighlighting_nested_rules() throws Exception {
-    SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder()
-      .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION)
-      .registerHighlightingRule(4, 6, TypeOfText.COMMENT)
-      .registerHighlightingRule(7, 16, TypeOfText.CONSTANT)
-      .registerHighlightingRule(8, 15, TypeOfText.KEYWORD)
-      .build();
-    when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting);
-    metadata.setOriginalLineOffsets(new int[] {0, 4, 7});
+    batchReportWriter.writeComponentSyntaxHighlighting(1, Arrays.asList(
+      newRule(1, 0, 1, 3, HighlightingType.ANNOTATION),
+      newRule(2, 0, 2, 2, HighlightingType.COMMENT),
+      newRule(3, 0, 3, 9, HighlightingType.CONSTANT),
+      newRule(3, 1, 3, 8, HighlightingType.KEYWORD)));
+
+    inputFile.setOriginalLineOffsets(new int[] {0, 4, 7});
 
-    sut.applyHighlighting(inputFile, metadata, output);
+    sut.applyHighlighting(inputFile, output);
 
     FileSourceDb.Data data = output.build();
     assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,3,a");
@@ -330,16 +299,15 @@ public class SourceDataFactoryTest {
 
   @Test
   public void applyHighlighting_nested_rules_and_multiple_lines() throws Exception {
-    SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder()
-      .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION)
-      .registerHighlightingRule(4, 6, TypeOfText.COMMENT)
-      .registerHighlightingRule(4, 16, TypeOfText.CONSTANT)
-      .registerHighlightingRule(8, 15, TypeOfText.KEYWORD)
-      .build();
-    when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting);
-    metadata.setOriginalLineOffsets(new int[] {0, 4, 7});
+    batchReportWriter.writeComponentSyntaxHighlighting(1, Arrays.asList(
+      newRule(1, 0, 1, 3, HighlightingType.ANNOTATION),
+      newRule(2, 0, 3, 9, HighlightingType.CONSTANT),
+      newRule(2, 0, 2, 2, HighlightingType.COMMENT),
+      newRule(3, 1, 3, 8, HighlightingType.KEYWORD)));
 
-    sut.applyHighlighting(inputFile, metadata, output);
+    inputFile.setOriginalLineOffsets(new int[] {0, 4, 7});
+
+    sut.applyHighlighting(inputFile, output);
 
     FileSourceDb.Data data = output.build();
     assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,3,a");
@@ -347,11 +315,26 @@ public class SourceDataFactoryTest {
     assertThat(data.getLines(2).getHighlighting()).isEqualTo("0,9,c;1,8,k");
   }
 
+  private HighlightingRule newRule(int startLine, int startOffset, int endLine, int endOffset, HighlightingType type) {
+    return BatchReport.SyntaxHighlighting.HighlightingRule.newBuilder()
+      .setRange(Range.newBuilder().setStartLine(startLine).setStartOffset(startOffset).setEndLine(endLine).setEndOffset(endOffset).build())
+      .setType(type)
+      .build();
+  }
+
+  private BatchReport.Symbols.Symbol newSymbol(int startLine, int startOffset, int endLine, int endOffset,
+    int startLine1, int startOffset1, int endLine1, int endOffset1,
+    int startLine2, int startOffset2, int endLine2, int endOffset2) {
+    return BatchReport.Symbols.Symbol.newBuilder()
+      .setDeclaration(Range.newBuilder().setStartLine(startLine).setStartOffset(startOffset).setEndLine(endLine).setEndOffset(endOffset).build())
+      .addReference(Range.newBuilder().setStartLine(startLine1).setStartOffset(startOffset1).setEndLine(endLine1).setEndOffset(endOffset1).build())
+      .addReference(Range.newBuilder().setStartLine(startLine2).setStartOffset(startOffset2).setEndLine(endLine2).setEndOffset(endOffset2).build())
+      .build();
+  }
+
   @Test
   public void applySymbolReferences_missing() throws Exception {
-    when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING)).thenReturn(null);
-
-    sut.applySymbolReferences(inputFile, metadata, output);
+    sut.applySymbolReferences(inputFile, output);
 
     FileSourceDb.Data data = output.build();
     assertThat(data.getLines(0).hasSymbols()).isFalse();
@@ -361,17 +344,17 @@ public class SourceDataFactoryTest {
 
   @Test
   public void applySymbolReferences() throws Exception {
-    DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(inputFile.key(), null);
-    Symbol s1 = symbolBuilder.newSymbol(1, 2);
-    symbolBuilder.newReference(s1, 4);
-    symbolBuilder.newReference(s1, 11);
-    Symbol s2 = symbolBuilder.newSymbol(4, 6);
-    symbolBuilder.newReference(s2, 0);
-    symbolBuilder.newReference(s2, 7);
-    when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING)).thenReturn(symbolBuilder.build());
-    metadata.setOriginalLineOffsets(new int[] {0, 4, 7});
-
-    sut.applySymbolReferences(inputFile, metadata, output);
+    batchReportWriter.writeComponentSymbols(1, Arrays.asList(
+      newSymbol(1, 1, 1, 2,
+        2, 0, 2, 1,
+        3, 4, 3, 5),
+      newSymbol(2, 0, 2, 2,
+        1, 0, 1, 2,
+        3, 0, 3, 2)
+      ));
+    inputFile.setOriginalLineOffsets(new int[] {0, 4, 7});
+
+    sut.applySymbolReferences(inputFile, output);
 
     FileSourceDb.Data data = output.build();
     assertThat(data.getLines(0).getSymbols()).isEqualTo("1,2,1;0,2,2");
@@ -381,17 +364,18 @@ public class SourceDataFactoryTest {
 
   @Test
   public void applySymbolReferences_declaration_order_is_not_important() throws Exception {
-    DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(inputFile.key(), null);
-    Symbol s2 = symbolBuilder.newSymbol(4, 6);
-    symbolBuilder.newReference(s2, 7);
-    symbolBuilder.newReference(s2, 0);
-    Symbol s1 = symbolBuilder.newSymbol(1, 2);
-    symbolBuilder.newReference(s1, 11);
-    symbolBuilder.newReference(s1, 4);
-    when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING)).thenReturn(symbolBuilder.build());
-    metadata.setOriginalLineOffsets(new int[] {0, 4, 7});
-
-    sut.applySymbolReferences(inputFile, metadata, output);
+    batchReportWriter.writeComponentSymbols(1, Arrays.asList(
+      newSymbol(2, 0, 2, 2,
+        1, 0, 1, 2,
+        3, 0, 3, 2),
+      newSymbol(1, 1, 1, 2,
+        2, 0, 2, 1,
+        3, 4, 3, 5)
+
+      ));
+    inputFile.setOriginalLineOffsets(new int[] {0, 4, 7});
+
+    sut.applySymbolReferences(inputFile, output);
 
     FileSourceDb.Data data = output.build();
     assertThat(data.getLines(0).getSymbols()).isEqualTo("1,2,1;0,2,2");
index e4dc6d3099f03cea10daef62288649952e6023d3..a0021e72025707737ab6316e87067f59b02bdd9f 100644 (file)
@@ -44,7 +44,6 @@ import org.sonar.api.rules.RuleFinder;
 import org.sonar.api.utils.Duration;
 import org.sonar.api.utils.System2;
 import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.scan.filesystem.InputFileMetadata;
 import org.sonar.batch.scan.filesystem.InputPathCache;
 import org.sonar.core.issue.IssueUpdater;
 import org.sonar.core.issue.db.IssueChangeDto;
@@ -60,19 +59,8 @@ import java.util.List;
 
 import static com.google.common.collect.Lists.newArrayList;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyCollection;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.RETURNS_MOCKS;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
 
 public class IssueTrackingDecoratorTest {
 
@@ -132,7 +120,6 @@ public class IssueTrackingDecoratorTest {
     List<ServerIssue> dbIssues = Collections.emptyList();
     when(initialOpenIssues.selectAndRemoveIssues("struts:Action.java")).thenReturn(dbIssues);
     when(inputPathCache.getFile("foo", "Action.java")).thenReturn(mock(DefaultInputFile.class));
-    when(inputPathCache.getFileMetadata("foo", "Action.java")).thenReturn(new InputFileMetadata());
     decorator.doDecorate(file);
 
     // Apply filters, track, apply transitions, notify extensions then update cache
@@ -161,7 +148,6 @@ public class IssueTrackingDecoratorTest {
 
     when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
     when(inputPathCache.getFile("foo", "Action.java")).thenReturn(mock(DefaultInputFile.class));
-    when(inputPathCache.getFileMetadata("foo", "Action.java")).thenReturn(new InputFileMetadata());
 
     decorator.doDecorate(file);
 
index 42f7bfc8330b20a7347545f6bdbe17d1f1115403..8b42d6a5b670d8336c90b78fecf6edaebaee4786 100644 (file)
@@ -71,7 +71,7 @@ public class HighlightingMediumTest {
 
     File xooFile = new File(srcDir, "sample.xoo");
     File xoohighlightingFile = new File(srcDir, "sample.xoo.highlighting");
-    FileUtils.write(xooFile, "Sample xoo\ncontent");
+    FileUtils.write(xooFile, "Sample xoo\ncontent plop");
     FileUtils.write(xoohighlightingFile, "0:10:s\n11:18:k");
 
     TaskResult result = tester.newTask()
@@ -87,10 +87,10 @@ public class HighlightingMediumTest {
       .start();
 
     InputFile file = result.inputFile("src/sample.xoo");
-    assertThat(result.highlightingTypeFor(file, 0)).containsExactly(TypeOfText.STRING);
-    assertThat(result.highlightingTypeFor(file, 9)).containsExactly(TypeOfText.STRING);
-    assertThat(result.highlightingTypeFor(file, 10)).isEmpty();
-    assertThat(result.highlightingTypeFor(file, 11)).containsExactly(TypeOfText.KEYWORD);
+    assertThat(result.highlightingTypeFor(file, 1, 0)).containsExactly(TypeOfText.STRING);
+    assertThat(result.highlightingTypeFor(file, 1, 9)).containsExactly(TypeOfText.STRING);
+    assertThat(result.highlightingTypeFor(file, 2, 0)).containsExactly(TypeOfText.KEYWORD);
+    assertThat(result.highlightingTypeFor(file, 2, 8)).isEmpty();
 
   }
 
@@ -126,7 +126,7 @@ public class HighlightingMediumTest {
     System.out.println("Duration: " + (System.currentTimeMillis() - start));
 
     InputFile file = result.inputFile("src/sample.xoo");
-    assertThat(result.highlightingTypeFor(file, 0)).containsExactly(TypeOfText.STRING);
+    assertThat(result.highlightingTypeFor(file, 1, 0)).containsExactly(TypeOfText.STRING);
 
   }
 
index a231c93f69b644d9d24923fcff1c83ca3423aa6e..b8ca587d1b96e465038b341d006d40a968d4912f 100644 (file)
@@ -28,6 +28,7 @@ import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.batch.protocol.output.BatchReport.Range;
 import org.sonar.xoo.XooPlugin;
 
 import java.io.File;
@@ -65,6 +66,7 @@ public class SymbolMediumTest {
     File xooFile = new File(srcDir, "sample.xoo");
     File xooSymbolFile = new File(srcDir, "sample.xoo.symbol");
     FileUtils.write(xooFile, "Sample xoo\ncontent\nanother xoo");
+    // Highlight xoo symbol
     FileUtils.write(xooSymbolFile, "7,10,27");
 
     TaskResult result = tester.newTask()
@@ -80,7 +82,7 @@ public class SymbolMediumTest {
       .start();
 
     InputFile file = result.inputFile("src/sample.xoo");
-    assertThat(result.symbolReferencesFor(file, 7, 10)).containsOnly(27);
+    assertThat(result.symbolReferencesFor(file, 1, 7)).containsOnly(Range.newBuilder().setStartLine(3).setStartOffset(8).setEndLine(3).setEndOffset(11).build());
   }
 
 }
diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/PublishReportJobTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/PublishReportJobTest.java
deleted file mode 100644 (file)
index a636a53..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.batch.report;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.api.config.Settings;
-import org.sonar.api.platform.Server;
-import org.sonar.api.utils.TempFolder;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
-import org.sonar.batch.bootstrap.ServerClient;
-import org.sonar.batch.index.ResourceCache;
-import org.sonar.jpa.test.AbstractDbUnitTestCase;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class PublishReportJobTest extends AbstractDbUnitTestCase {
-
-  private DefaultAnalysisMode mode;
-
-  ResourceCache resourceCache = mock(ResourceCache.class);
-
-  private ProjectReactor reactor;
-
-  @Before
-  public void setUp() {
-    mode = mock(DefaultAnalysisMode.class);
-    reactor = mock(ProjectReactor.class);
-    when(reactor.getRoot()).thenReturn(ProjectDefinition.create().setKey("struts"));
-  }
-
-  @Test
-  public void should_log_successful_analysis() throws Exception {
-    Settings settings = new Settings();
-    settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/");
-    PublishReportJob job = new PublishReportJob(settings, mock(ServerClient.class), mock(Server.class), reactor, mode, mock(TempFolder.class), new ReportPublisher[0]);
-
-    Logger logger = mock(Logger.class);
-    job.logSuccess(logger);
-
-    verify(logger).info("ANALYSIS SUCCESSFUL, you can browse {}", "http://myserver/dashboard/index/struts");
-    verify(logger).info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report.");
-  }
-
-  @Test
-  public void should_log_successful_preview_analysis() throws Exception {
-    Settings settings = new Settings();
-    when(mode.isPreview()).thenReturn(true);
-    PublishReportJob job = new PublishReportJob(settings, mock(ServerClient.class), mock(Server.class), reactor, mode, mock(TempFolder.class), new ReportPublisher[0]);
-
-    Logger logger = mock(Logger.class);
-    job.logSuccess(logger);
-
-    verify(logger).info("ANALYSIS SUCCESSFUL");
-  }
-
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java
new file mode 100644 (file)
index 0000000..5b9221d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.batch.report;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.config.Settings;
+import org.sonar.api.platform.Server;
+import org.sonar.api.utils.TempFolder;
+import org.sonar.batch.bootstrap.DefaultAnalysisMode;
+import org.sonar.batch.bootstrap.ServerClient;
+import org.sonar.batch.index.ResourceCache;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class ReportPublisherTest extends AbstractDbUnitTestCase {
+
+  private DefaultAnalysisMode mode;
+
+  ResourceCache resourceCache = mock(ResourceCache.class);
+
+  private ProjectReactor reactor;
+
+  @Before
+  public void setUp() {
+    mode = mock(DefaultAnalysisMode.class);
+    reactor = mock(ProjectReactor.class);
+    when(reactor.getRoot()).thenReturn(ProjectDefinition.create().setKey("struts"));
+  }
+
+  @Test
+  public void should_log_successful_analysis() throws Exception {
+    Settings settings = new Settings();
+    settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/");
+    ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+
+    Logger logger = mock(Logger.class);
+    job.logSuccess(logger);
+
+    verify(logger).info("ANALYSIS SUCCESSFUL, you can browse {}", "http://myserver/dashboard/index/struts");
+    verify(logger).info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report.");
+  }
+
+  @Test
+  public void should_log_successful_preview_analysis() throws Exception {
+    Settings settings = new Settings();
+    when(mode.isPreview()).thenReturn(true);
+    ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+
+    Logger logger = mock(Logger.class);
+    job.logSuccess(logger);
+
+    verify(logger).info("ANALYSIS SUCCESSFUL");
+  }
+
+}
index 5960fab5c2d0aec129be3a6e800b0b6fcf2bc4f1..c62ccb0ae8cc21a5c43a2cde9b59c2d2ea9ff728 100644 (file)
@@ -30,15 +30,14 @@ import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
-import org.sonar.api.resources.AbstractLanguage;
-import org.sonar.api.resources.Java;
-import org.sonar.api.resources.Languages;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.*;
+import org.sonar.batch.index.BatchResource;
+import org.sonar.batch.index.ResourceCache;
 
 import java.io.File;
 import java.io.IOException;
 
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -97,7 +96,9 @@ public class ComponentIndexerTest {
   }
 
   private ComponentIndexer createIndexer(Languages languages) {
-    return new ComponentIndexer(project, languages, sonarIndex);
+    ResourceCache resourceCache = mock(ResourceCache.class);
+    when(resourceCache.get(any(Resource.class))).thenReturn(new BatchResource(1, org.sonar.api.resources.File.create("foo.php"), null));
+    return new ComponentIndexer(project, languages, sonarIndex, resourceCache);
   }
 
   @Test
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileMetadataTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileMetadataTest.java
deleted file mode 100644 (file)
index a470378..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * 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.batch.scan.filesystem;
-
-import com.google.common.base.Charsets;
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.io.FileUtils;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.AnalysisMode;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.batch.scan.filesystem.FileMetadata.LineHashConsumer;
-
-import javax.annotation.Nullable;
-
-import java.io.File;
-import java.nio.charset.Charset;
-
-import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class FileMetadataTest {
-
-  @Rule
-  public ExpectedException thrown = ExpectedException.none();
-
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  private AnalysisMode mode = mock(AnalysisMode.class);
-
-  @Rule
-  public LogTester logTester = new LogTester();
-
-  @Test
-  public void empty_file() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.touch(tempFile);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(1);
-    assertThat(metadata.nonBlankLines).isEqualTo(0);
-    assertThat(metadata.hash).isNotEmpty();
-    assertThat(metadata.originalLineOffsets).containsOnly(0);
-    assertThat(metadata.lastValidOffset).isEqualTo(0);
-    assertThat(metadata.empty).isTrue();
-  }
-
-  @Test
-  public void windows_without_latest_eol() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "foo\r\nbar\r\nbaz", Charsets.UTF_8, true);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(3);
-    assertThat(metadata.nonBlankLines).isEqualTo(3);
-    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 5, 10);
-    assertThat(metadata.lastValidOffset).isEqualTo(13);
-    assertThat(metadata.empty).isFalse();
-  }
-
-  @Test
-  public void read_with_wrong_encoding() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "marker´s\n", Charset.forName("cp1252"));
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(2);
-    assertThat(metadata.hash).isEqualTo(md5Hex("marker\ufffds\n"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 9);
-  }
-
-  @Test
-  public void non_ascii_utf_8() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "föo\r\nbàr\r\n\u1D11Ebaßz\r\n", Charsets.UTF_8, true);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(4);
-    assertThat(metadata.nonBlankLines).isEqualTo(3);
-    assertThat(metadata.hash).isEqualTo(md5Hex("föo\nbàr\n\u1D11Ebaßz\n"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 5, 10, 18);
-  }
-
-  @Test
-  public void non_ascii_utf_16() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "föo\r\nbàr\r\n\u1D11Ebaßz\r\n", Charsets.UTF_16, true);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_16);
-    assertThat(metadata.lines).isEqualTo(4);
-    assertThat(metadata.nonBlankLines).isEqualTo(3);
-    assertThat(metadata.hash).isEqualTo(md5Hex("föo\nbàr\n\u1D11Ebaßz\n"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 5, 10, 18);
-  }
-
-  @Test
-  public void unix_without_latest_eol() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "foo\nbar\nbaz", Charsets.UTF_8, true);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(3);
-    assertThat(metadata.nonBlankLines).isEqualTo(3);
-    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 8);
-    assertThat(metadata.lastValidOffset).isEqualTo(11);
-  }
-
-  @Test
-  public void unix_with_latest_eol() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "foo\nbar\nbaz\n", Charsets.UTF_8, true);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(4);
-    assertThat(metadata.nonBlankLines).isEqualTo(3);
-    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz\n"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 8, 12);
-    assertThat(metadata.lastValidOffset).isEqualTo(12);
-  }
-
-  @Test
-  public void mix_of_newlines_with_latest_eol() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "foo\nbar\r\nbaz\n", Charsets.UTF_8, true);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(4);
-    assertThat(metadata.nonBlankLines).isEqualTo(3);
-    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz\n"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 9, 13);
-  }
-
-  @Test
-  public void several_new_lines() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "foo\n\n\nbar", Charsets.UTF_8, true);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(4);
-    assertThat(metadata.nonBlankLines).isEqualTo(2);
-    assertThat(metadata.hash).isEqualTo(md5Hex("foo\n\n\nbar"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 5, 6);
-  }
-
-  @Test
-  public void mix_of_newlines_without_latest_eol() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "foo\nbar\r\nbaz", Charsets.UTF_8, true);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(3);
-    assertThat(metadata.nonBlankLines).isEqualTo(3);
-    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 9);
-  }
-
-  @Test
-  public void start_with_newline() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "\nfoo\nbar\r\nbaz", Charsets.UTF_8, true);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(4);
-    assertThat(metadata.nonBlankLines).isEqualTo(3);
-    assertThat(metadata.hash).isEqualTo(md5Hex("\nfoo\nbar\nbaz"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 1, 5, 10);
-  }
-
-  @Test
-  public void start_with_bom() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, "\uFEFFfoo\nbar\r\nbaz", Charsets.UTF_8, true);
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(3);
-    assertThat(metadata.nonBlankLines).isEqualTo(3);
-    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz"));
-    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 9);
-  }
-
-  @Test
-  public void ignore_whitespace_when_computing_line_hashes() throws Exception {
-    File tempFile = temp.newFile();
-    FileUtils.write(tempFile, " foo\nb ar\r\nbaz \t", Charsets.UTF_8, true);
-
-    DefaultInputFile f = new DefaultInputFile("foo", tempFile.getName());
-    f.setModuleBaseDir(tempFile.getParentFile().toPath());
-    f.setCharset(Charsets.UTF_8);
-    FileMetadata.computeLineHashesForIssueTracking(f, new LineHashConsumer() {
-
-      @Override
-      public void consume(int lineIdx, @Nullable byte[] hash) {
-        switch (lineIdx) {
-          case 1:
-            assertThat(Hex.encodeHexString(hash)).isEqualTo(md5Hex("foo"));
-            break;
-          case 2:
-            assertThat(Hex.encodeHexString(hash)).isEqualTo(md5Hex("bar"));
-            break;
-          case 3:
-            assertThat(Hex.encodeHexString(hash)).isEqualTo(md5Hex("baz"));
-            break;
-        }
-      }
-    });
-  }
-
-  @Test
-  public void should_throw_if_file_does_not_exist() throws Exception {
-    File tempFolder = temp.newFolder();
-    File file = new File(tempFolder, "doesNotExist.txt");
-
-    thrown.expect(IllegalStateException.class);
-    thrown.expectMessage("Fail to read file '" + file.getAbsolutePath() + "' with encoding 'UTF-8'");
-
-    new FileMetadata(mode).read(file, Charsets.UTF_8);
-  }
-
-  @Test
-  public void line_feed_is_included_into_hash() throws Exception {
-    File file1 = temp.newFile();
-    FileUtils.write(file1, "foo\nbar\n", Charsets.UTF_8, true);
-
-    // same as file1, except an additional return carriage
-    File file1a = temp.newFile();
-    FileUtils.write(file1a, "foo\r\nbar\n", Charsets.UTF_8, true);
-
-    File file2 = temp.newFile();
-    FileUtils.write(file2, "foo\nbar", Charsets.UTF_8, true);
-
-    String hash1 = new FileMetadata(mode).read(file1, Charsets.UTF_8).hash;
-    String hash1a = new FileMetadata(mode).read(file1a, Charsets.UTF_8).hash;
-    String hash2 = new FileMetadata(mode).read(file2, Charsets.UTF_8).hash;
-    assertThat(hash1).isEqualTo(hash1a);
-    assertThat(hash1).isNotEqualTo(hash2);
-  }
-
-  @Test
-  public void binary_file_with_unmappable_character() throws Exception {
-    File woff = new File(this.getClass().getResource("glyphicons-halflings-regular.woff").toURI());
-
-    FileMetadata.Metadata metadata = new FileMetadata(mode).read(woff, Charsets.UTF_8);
-    assertThat(metadata.lines).isEqualTo(135);
-    assertThat(metadata.nonBlankLines).isEqualTo(134);
-    assertThat(metadata.hash).isNotEmpty();
-    assertThat(metadata.empty).isFalse();
-
-    assertThat(logTester.logs(LoggerLevel.WARN).get(0)).contains("Invalid character encountered in file");
-    assertThat(logTester.logs(LoggerLevel.WARN).get(0)).contains(
-      "glyphicons-halflings-regular.woff at line 1 for encoding UTF-8. Please fix file content or configure the encoding to be used using property 'sonar.sourceEncoding'.");
-  }
-
-}
index 2b5b1f4bcca6fcec0a1c7efaabfb4de3733a414b..0c83abfa01e0a4e94073e310d5e8ae6895a60c3a 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.batch.scan.filesystem;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.config.Settings;
 import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.batch.bootstrap.DefaultAnalysisMode;
@@ -39,7 +40,7 @@ public class InputFileBuilderFactoryTest {
     DefaultAnalysisMode analysisMode = mock(DefaultAnalysisMode.class);
 
     InputFileBuilderFactory factory = new InputFileBuilderFactory(ProjectDefinition.create().setKey("struts"), pathResolver, langDetectionFactory,
-      statusDetectionFactory, analysisMode, new Settings(), new FileMetadata(analysisMode));
+      statusDetectionFactory, analysisMode, new Settings(), new FileMetadata());
     InputFileBuilder builder = factory.create(fs);
 
     assertThat(builder.langDetection()).isNotNull();
index df058f13c8f0f38c21733cfa4d361436135d2499..2438c3de381b3ff84d0ab013b6f09ea5efd5222a 100644 (file)
@@ -26,6 +26,7 @@ import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.config.Settings;
 import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.api.utils.PathUtils;
@@ -66,7 +67,7 @@ public class InputFileBuilderTest {
       .thenReturn(InputFile.Status.ADDED);
 
     InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(),
-      langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata(analysisMode));
+      langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata());
     DeprecatedDefaultInputFile inputFile = builder.create(srcFile);
     builder.completeAndComputeMetadata(inputFile, InputFile.Type.MAIN);
 
@@ -89,7 +90,7 @@ public class InputFileBuilderTest {
     when(fs.baseDir()).thenReturn(basedir);
 
     InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(),
-      langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata(analysisMode));
+      langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata());
     DeprecatedDefaultInputFile inputFile = builder.create(srcFile);
 
     assertThat(inputFile).isNull();
@@ -109,11 +110,11 @@ public class InputFileBuilderTest {
     when(langDetection.language(any(InputFile.class))).thenReturn(null);
 
     InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(),
-      langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata(analysisMode));
+      langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata());
     DeprecatedDefaultInputFile inputFile = builder.create(srcFile);
-    InputFileMetadata metadata = builder.completeAndComputeMetadata(inputFile, InputFile.Type.MAIN);
+    inputFile = builder.completeAndComputeMetadata(inputFile, InputFile.Type.MAIN);
 
-    assertThat(metadata).isNull();
+    assertThat(inputFile).isNull();
   }
 
 }
index 7c943e06f66ff4dc9971e98ed0378b0ee1f59f7a..f14e2a9b957822bf0858d10eff377d17789ed5cf 100644 (file)
@@ -79,20 +79,4 @@ public class InputPathCacheTest {
     assertThat(cache.allFiles()).hasSize(1);
   }
 
-  @Test
-  public void should_add_input_file_metadata() throws Exception {
-    InputPathCache cache = new InputPathCache();
-    cache.put("struts-core", "src/main/java/Bar.java", new InputFileMetadata()
-      .setHash("xyz")
-      .setNonBlankLines(2)
-      .setEmpty(true)
-      .setOriginalLineOffsets(new int[] {0, 4}));
-
-    InputFileMetadata loadedFileMetadata = cache.getFileMetadata("struts-core", "src/main/java/Bar.java");
-    assertThat(loadedFileMetadata.originalLineOffsets()).containsOnly(0, 4);
-    assertThat(loadedFileMetadata.hash()).isEqualTo("xyz");
-    assertThat(loadedFileMetadata.nonBlankLines()).isEqualTo(2);
-    assertThat(loadedFileMetadata.isEmpty()).isTrue();
-
-  }
 }
index b65d7e8050c4a454216b4f85ff01f42a529779ef..60f04a9f6266dc34083b026a7c4b8d4a28c6264a 100644 (file)
@@ -38,11 +38,8 @@ import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency;
 import org.sonar.api.batch.sensor.issue.Issue.Severity;
 import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
-import org.sonar.api.component.ResourcePerspectives;
 import org.sonar.api.config.Settings;
 import org.sonar.api.design.Dependency;
-import org.sonar.api.issue.Issuable;
-import org.sonar.api.issue.Issue;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.PersistenceMode;
@@ -52,9 +49,10 @@ import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.batch.duplication.DuplicationCache;
-import org.sonar.batch.index.ComponentDataCache;
 import org.sonar.batch.index.DefaultIndex;
 import org.sonar.batch.index.ResourceCache;
+import org.sonar.batch.issue.ModuleIssues;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.sensor.coverage.CoverageExclusions;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -77,7 +75,7 @@ public class DefaultSensorStorageTest {
   private DefaultFileSystem fs;
   private DefaultSensorStorage sensorStorage;
   private Settings settings;
-  private ResourcePerspectives resourcePerspectives;
+  private ModuleIssues moduleIssues;
   private Project project;
   private DefaultIndex sonarIndex;
 
@@ -91,15 +89,14 @@ public class DefaultSensorStorageTest {
     when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC);
     when(metricFinder.findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
     settings = new Settings();
-    resourcePerspectives = mock(ResourcePerspectives.class);
-    ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
+    moduleIssues = mock(ModuleIssues.class);
     project = new Project("myProject");
     sonarIndex = mock(DefaultIndex.class);
     CoverageExclusions coverageExclusions = mock(CoverageExclusions.class);
     when(coverageExclusions.accept(any(Resource.class), any(Measure.class))).thenReturn(true);
     resourceCache = new ResourceCache();
     sensorStorage = new DefaultSensorStorage(metricFinder, project,
-      resourcePerspectives, settings, fs, activeRules, componentDataCache, mock(DuplicationCache.class), sonarIndex, coverageExclusions, resourceCache);
+      moduleIssues, settings, fs, activeRules, mock(DuplicationCache.class), sonarIndex, coverageExclusions, resourceCache, mock(ReportPublisher.class));
   }
 
   @Test
@@ -178,12 +175,7 @@ public class DefaultSensorStorageTest {
   public void shouldAddIssueOnFile() {
     InputFile file = new DefaultInputFile("foo", "src/Foo.php").setLines(4);
 
-    ArgumentCaptor<Issue> argumentCaptor = ArgumentCaptor.forClass(Issue.class);
-
-    Issuable issuable = mock(Issuable.class);
-    when(resourcePerspectives.as(Issuable.class, File.create("src/Foo.php"))).thenReturn(issuable);
-
-    when(issuable.addIssue(argumentCaptor.capture())).thenReturn(true);
+    ArgumentCaptor<org.sonar.api.issue.internal.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.issue.internal.DefaultIssue.class);
 
     sensorStorage.store(new DefaultIssue()
       .onFile(file)
@@ -192,7 +184,9 @@ public class DefaultSensorStorageTest {
       .atLine(3)
       .effortToFix(10.0));
 
-    Issue issue = argumentCaptor.getValue();
+    verify(moduleIssues).initAndAddIssue(argumentCaptor.capture());
+
+    org.sonar.api.issue.internal.DefaultIssue issue = argumentCaptor.getValue();
     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
     assertThat(issue.message()).isEqualTo("Foo");
     assertThat(issue.line()).isEqualTo(3);
@@ -204,12 +198,7 @@ public class DefaultSensorStorageTest {
   public void shouldAddIssueOnDirectory() {
     InputDir dir = new DefaultInputDir("foo", "src");
 
-    ArgumentCaptor<Issue> argumentCaptor = ArgumentCaptor.forClass(Issue.class);
-
-    Issuable issuable = mock(Issuable.class);
-    when(resourcePerspectives.as(Issuable.class, Directory.create("src"))).thenReturn(issuable);
-
-    when(issuable.addIssue(argumentCaptor.capture())).thenReturn(true);
+    ArgumentCaptor<org.sonar.api.issue.internal.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.issue.internal.DefaultIssue.class);
 
     sensorStorage.store(new DefaultIssue()
       .onDir(dir)
@@ -217,7 +206,9 @@ public class DefaultSensorStorageTest {
       .message("Foo")
       .effortToFix(10.0));
 
-    Issue issue = argumentCaptor.getValue();
+    verify(moduleIssues).initAndAddIssue(argumentCaptor.capture());
+
+    org.sonar.api.issue.internal.DefaultIssue issue = argumentCaptor.getValue();
     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
     assertThat(issue.message()).isEqualTo("Foo");
     assertThat(issue.line()).isNull();
@@ -227,12 +218,7 @@ public class DefaultSensorStorageTest {
 
   @Test
   public void shouldAddIssueOnProject() {
-    ArgumentCaptor<Issue> argumentCaptor = ArgumentCaptor.forClass(Issue.class);
-
-    Issuable issuable = mock(Issuable.class);
-    when(resourcePerspectives.as(Issuable.class, (Resource) project)).thenReturn(issuable);
-
-    when(issuable.addIssue(argumentCaptor.capture())).thenReturn(true);
+    ArgumentCaptor<org.sonar.api.issue.internal.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.issue.internal.DefaultIssue.class);
 
     sensorStorage.store(new DefaultIssue()
       .onProject()
@@ -241,7 +227,9 @@ public class DefaultSensorStorageTest {
       .overrideSeverity(Severity.BLOCKER)
       .effortToFix(10.0));
 
-    Issue issue = argumentCaptor.getValue();
+    verify(moduleIssues).initAndAddIssue(argumentCaptor.capture());
+
+    org.sonar.api.issue.internal.DefaultIssue issue = argumentCaptor.getValue();
     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
     assertThat(issue.message()).isEqualTo("Foo");
     assertThat(issue.line()).isNull();
index a7486ad376b0f69d523ca39a52872b83b407f06a..a656866163310aa547ac13348f996ef10a116d5f 100644 (file)
@@ -25,38 +25,45 @@ import org.apache.commons.io.FileUtils;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
+import org.sonar.api.batch.sensor.highlighting.TypeOfText;
 import org.sonar.api.web.CodeColorizerFormat;
-import org.sonar.batch.highlighting.SyntaxHighlightingData;
-import org.sonar.colorizer.CDocTokenizer;
-import org.sonar.colorizer.CppDocTokenizer;
-import org.sonar.colorizer.JavadocTokenizer;
-import org.sonar.colorizer.KeywordsTokenizer;
-import org.sonar.colorizer.StringTokenizer;
-import org.sonar.colorizer.Tokenizer;
+import org.sonar.colorizer.*;
 
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
 
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 public class CodeColorizersTest {
 
-  private static final String HIGHLIGHTING_JS = "0,4,cppd;5,11,cppd;12,15,cppd;16,19,k;29,37,k;65,69,k;85,93,cd;98,102,k;112,114,s;120,124,k";
-  private static final String HIGHLIGHTING_JAVA = "0,4,j;5,11,j;12,15,j;16,22,k;23,28,k;43,50,k;51,54,k;67,78,a;81,87,k;88,92,k;97,100,k;142,146,k;162,170,cd";
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
 
   @Test
   public void testConvertToHighlighting() throws Exception {
     CodeColorizers codeColorizers = new CodeColorizers(Arrays.<CodeColorizerFormat>asList(new JavaScriptColorizerFormat()));
-
     File jsFile = new File(this.getClass().getResource("CodeColorizersTest/Person.js").toURI());
+    NewHighlighting highlighting = mock(NewHighlighting.class);
 
-    SyntaxHighlightingData syntaxHighlighting = codeColorizers.toSyntaxHighlighting(jsFile, Charsets.UTF_8, "js");
+    codeColorizers.toSyntaxHighlighting(jsFile, Charsets.UTF_8, "js", highlighting);
 
-    assertThat(syntaxHighlighting.writeString()).isEqualTo(HIGHLIGHTING_JS);
+    verifyForJs(highlighting);
+  }
 
+  private void verifyForJs(NewHighlighting highlighting) {
+    verify(highlighting).highlight(0, 4, TypeOfText.CPP_DOC);
+    verify(highlighting).highlight(5, 11, TypeOfText.CPP_DOC);
+    verify(highlighting).highlight(12, 15, TypeOfText.CPP_DOC);
+    verify(highlighting).highlight(16, 19, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(29, 37, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(65, 69, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(85, 93, TypeOfText.COMMENT);
+    verify(highlighting).highlight(98, 102, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(112, 114, TypeOfText.STRING);
+    verify(highlighting).highlight(120, 124, TypeOfText.KEYWORD);
   }
 
   @Test
@@ -68,9 +75,10 @@ public class CodeColorizersTest {
     File jsFile = new File(this.getClass().getResource("CodeColorizersTest/Person.js").toURI());
     FileUtils.write(fileWithBom, FileUtils.readFileToString(jsFile), "UTF-8", true);
 
-    SyntaxHighlightingData syntaxHighlighting = codeColorizers.toSyntaxHighlighting(fileWithBom, Charsets.UTF_8, "js");
+    NewHighlighting highlighting = mock(NewHighlighting.class);
+    codeColorizers.toSyntaxHighlighting(fileWithBom, Charsets.UTF_8, "js", highlighting);
 
-    assertThat(syntaxHighlighting.writeString()).isEqualTo(HIGHLIGHTING_JS);
+    verifyForJs(highlighting);
   }
 
   @Test
@@ -79,9 +87,22 @@ public class CodeColorizersTest {
 
     File javaFile = new File(this.getClass().getResource("CodeColorizersTest/Person.java").toURI());
 
-    SyntaxHighlightingData syntaxHighlighting = codeColorizers.toSyntaxHighlighting(javaFile, Charsets.UTF_8, "java");
-
-    assertThat(syntaxHighlighting.writeString()).isEqualTo(HIGHLIGHTING_JAVA);
+    NewHighlighting highlighting = mock(NewHighlighting.class);
+    codeColorizers.toSyntaxHighlighting(javaFile, Charsets.UTF_8, "java", highlighting);
+
+    verify(highlighting).highlight(0, 4, TypeOfText.STRUCTURED_COMMENT);
+    verify(highlighting).highlight(5, 11, TypeOfText.STRUCTURED_COMMENT);
+    verify(highlighting).highlight(12, 15, TypeOfText.STRUCTURED_COMMENT);
+    verify(highlighting).highlight(16, 22, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(23, 28, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(43, 50, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(51, 54, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(67, 78, TypeOfText.ANNOTATION);
+    verify(highlighting).highlight(81, 87, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(88, 92, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(97, 100, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(142, 146, TypeOfText.KEYWORD);
+    verify(highlighting).highlight(162, 170, TypeOfText.COMMENT);
 
   }
 
index a14e4fe91d19efaeedb388bb77a22b42d3ef7f1a..7b3d3987bf34ecdadff32455019090234231fbd1 100644 (file)
@@ -23,16 +23,16 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.mockito.ArgumentCaptor;
-import org.sonar.api.component.Component;
-import org.sonar.batch.highlighting.SyntaxHighlightingData;
-import org.sonar.batch.index.ComponentDataCache;
-import org.sonar.core.source.SnapshotDataTypes;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
+import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+
+import java.io.StringReader;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 public class DefaultHighlightableTest {
 
@@ -41,27 +41,15 @@ public class DefaultHighlightableTest {
 
   @Test
   public void should_store_highlighting_rules() throws Exception {
-    DefaultHighlightable highlightablePerspective = new DefaultHighlightable(mock(Component.class), null);
-    highlightablePerspective.newHighlighting().highlight(0, 10, "k").highlight(20, 30, "cppd");
-
-    assertThat(highlightablePerspective.getHighlightingRules().getSyntaxHighlightingRuleSet()).hasSize(2);
+    SensorStorage sensorStorage = mock(SensorStorage.class);
+    DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php")
+      .initMetadata(new FileMetadata().readMetadata(new StringReader("azerty\nbla bla")));
+    DefaultHighlightable highlightablePerspective = new DefaultHighlightable(inputFile, sensorStorage);
+    highlightablePerspective.newHighlighting().highlight(0, 6, "k").highlight(7, 10, "cppd").done();
+
+    ArgumentCaptor<DefaultHighlighting> argCaptor = ArgumentCaptor.forClass(DefaultHighlighting.class);
+    verify(sensorStorage).store(argCaptor.capture());
+    assertThat(argCaptor.getValue().getSyntaxHighlightingRuleSet()).hasSize(2);
   }
 
-  @Test
-  public void should_apply_registered_highlighting() throws Exception {
-    Component component = mock(Component.class);
-    when(component.key()).thenReturn("myComponent");
-
-    ComponentDataCache cache = mock(ComponentDataCache.class);
-
-    DefaultHighlightable highlightable = new DefaultHighlightable(component, cache);
-    highlightable.newHighlighting()
-      .highlight(0, 10, "k")
-      .highlight(20, 30, "cppd")
-      .done();
-
-    ArgumentCaptor<SyntaxHighlightingData> argCaptor = ArgumentCaptor.forClass(SyntaxHighlightingData.class);
-    verify(cache).setData(eq("myComponent"), eq(SnapshotDataTypes.SYNTAX_HIGHLIGHTING), argCaptor.capture());
-    assertThat(argCaptor.getValue().writeString()).isEqualTo("0,10,k;20,30,cppd");
-  }
 }
index c23fcd2831ddf148ae63db4282ff3be28d229c50..a0489a7c2098c1c0f537454a60376a71bffd588d 100644 (file)
 
 package org.sonar.batch.source;
 
+import com.google.common.base.Strings;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.source.Symbol;
 import org.sonar.api.source.Symbolizable;
 
+import java.io.StringReader;
+
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class DefaultSymbolTableTest {
 
   @Rule
   public ExpectedException throwable = ExpectedException.none();
+  private DefaultInputFile inputFile;
+
+  @Before
+  public void prepare() {
+    inputFile = new DefaultInputFile("foo", "src/Foo.php")
+      .initMetadata(new FileMetadata().readMetadata(new StringReader(Strings.repeat("azerty\n", 20))));
+  }
 
   @Test
   public void should_order_symbol_and_references() throws Exception {
-    Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder("foo");
+
+    Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile);
     Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20);
     symbolTableBuilder.newReference(firstSymbol, 32);
     Symbol secondSymbol = symbolTableBuilder.newSymbol(84, 92);
@@ -45,25 +59,22 @@ public class DefaultSymbolTableTest {
     Symbolizable.SymbolTable symbolTable = symbolTableBuilder.build();
 
     assertThat(symbolTable.symbols()).containsExactly(firstSymbol, secondSymbol, thirdSymbol);
-    assertThat(symbolTable.references(firstSymbol)).containsExactly(32);
-    assertThat(symbolTable.references(secondSymbol)).containsExactly(124);
-    assertThat(symbolTable.references(thirdSymbol)).containsExactly(70);
   }
 
   @Test
   public void should_reject_reference_conflicting_with_declaration() throws Exception {
     throwable.expect(UnsupportedOperationException.class);
 
-    Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder("foo");
+    Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile);
     Symbol symbol = symbolTableBuilder.newSymbol(10, 20);
     symbolTableBuilder.newReference(symbol, 15);
   }
 
   @Test
   public void test_toString() throws Exception {
-    Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder("foo");
+    Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile);
     Symbol symbol = symbolTableBuilder.newSymbol(10, 20);
 
-    assertThat(symbol.toString()).isEqualTo("Symbol{offset=10-20}");
+    assertThat(symbol.toString()).isEqualTo("Symbol{range=Range[from [line=2, lineOffset=3] to [line=3, lineOffset=6]]}");
   }
 }
index 940c47ed46e176026e3a5797a31f031630ff7d42..d3b1018ace578768f3d990341a669c25330f1fe7 100644 (file)
 
 package org.sonar.batch.source;
 
+import com.google.common.base.Strings;
 import org.junit.Test;
-import org.sonar.api.component.Component;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.source.Symbol;
 import org.sonar.api.source.Symbolizable;
-import org.sonar.batch.index.ComponentDataCache;
-import org.sonar.batch.symbol.SymbolData;
-import org.sonar.core.source.SnapshotDataTypes;
+import org.sonar.batch.sensor.DefaultSensorStorage;
 
-import static org.mockito.Matchers.any;
+import java.io.StringReader;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 public class DefaultSymbolizableTest {
 
   @Test
   public void should_update_cache_when_done() throws Exception {
 
-    Component component = mock(Component.class);
-    when(component.key()).thenReturn("myComponent");
-
-    ComponentDataCache cache = mock(ComponentDataCache.class);
+    DefaultSensorStorage sensorStorage = mock(DefaultSensorStorage.class);
+    DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php")
+      .initMetadata(new FileMetadata().readMetadata(new StringReader(Strings.repeat("azerty\n", 20))));
 
-    DefaultSymbolizable symbolPerspective = new DefaultSymbolizable(cache, component);
+    DefaultSymbolizable symbolPerspective = new DefaultSymbolizable(inputFile, sensorStorage);
     Symbolizable.SymbolTableBuilder symbolTableBuilder = symbolPerspective.newSymbolTableBuilder();
     Symbol firstSymbol = symbolTableBuilder.newSymbol(4, 8);
     symbolTableBuilder.newReference(firstSymbol, 12);
@@ -57,6 +59,9 @@ public class DefaultSymbolizableTest {
 
     symbolPerspective.setSymbolTable(symbolTable);
 
-    verify(cache).setData(eq("myComponent"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), any(SymbolData.class));
+    ArgumentCaptor<Map> argCaptor = ArgumentCaptor.forClass(Map.class);
+    verify(sensorStorage).store(eq(inputFile), argCaptor.capture());
+    // Map<Symbol, Set<TextRange>>
+    assertThat(argCaptor.getValue().keySet()).hasSize(2);
   }
 }
index e2adf2dd0db07cc7ab5e8c42d6ffa16fc053b053..893957a80a5760ee219ea0bf699b1296a0c1b2c9 100644 (file)
 package org.sonar.batch.source;
 
 import org.junit.Test;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
 import org.sonar.api.component.Component;
 import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.source.Highlightable;
-import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.batch.index.BatchResource;
+import org.sonar.batch.index.ResourceCache;
 import org.sonar.core.component.ResourceComponent;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class HighlightableBuilderTest {
 
-  ComponentDataCache cache = mock(ComponentDataCache.class);
-
   @Test
   public void should_load_default_perspective() throws Exception {
     Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c");
     Component component = new ResourceComponent(file);
 
-    HighlightableBuilder builder = new HighlightableBuilder(cache);
+    ResourceCache resourceCache = mock(ResourceCache.class);
+    when(resourceCache.get(file.getEffectiveKey())).thenReturn(new BatchResource(1, file, null).setInputPath(new DefaultInputFile("myproject", "path/to/foo.c")));
+    HighlightableBuilder builder = new HighlightableBuilder(resourceCache, mock(SensorStorage.class));
     Highlightable perspective = builder.loadPerspective(Highlightable.class, component);
 
     assertThat(perspective).isNotNull().isInstanceOf(DefaultHighlightable.class);
-    assertThat(perspective.component()).isSameAs(component);
+    assertThat(perspective.component().key()).isEqualTo(component.key());
   }
 
   @Test
   public void project_should_not_be_highlightable() {
     Component component = new ResourceComponent(new Project("struts").setEffectiveKey("org.struts"));
 
-    HighlightableBuilder builder = new HighlightableBuilder(cache);
+    HighlightableBuilder builder = new HighlightableBuilder(mock(ResourceCache.class), mock(SensorStorage.class));
     Highlightable perspective = builder.loadPerspective(Highlightable.class, component);
 
     assertThat(perspective).isNull();
index f2138c9a721d283d719d0e199f42bb4658841c95..16c4fe669a76ef035531996937c4a0e689a16a2f 100644 (file)
 package org.sonar.batch.source;
 
 import org.junit.Test;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.component.Component;
 import org.sonar.api.component.Perspective;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
 import org.sonar.api.source.Symbolizable;
-import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.batch.index.BatchResource;
+import org.sonar.batch.index.ResourceCache;
+import org.sonar.batch.sensor.DefaultSensorStorage;
+import org.sonar.core.component.ResourceComponent;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class SymbolizableBuilderTest {
 
-  ComponentDataCache dataCache = mock(ComponentDataCache.class);
-
   @Test
   public void should_load_perspective() throws Exception {
-    Component component = mock(Component.class);
+    Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c");
+    Component component = new ResourceComponent(file);
+
+    ResourceCache resourceCache = mock(ResourceCache.class);
+    when(resourceCache.get(file.getEffectiveKey())).thenReturn(new BatchResource(1, file, null).setInputPath(new DefaultInputFile("myproject", "path/to/foo.c")));
 
-    SymbolizableBuilder perspectiveBuilder = new SymbolizableBuilder(dataCache);
+    SymbolizableBuilder perspectiveBuilder = new SymbolizableBuilder(resourceCache, mock(DefaultSensorStorage.class));
     Perspective perspective = perspectiveBuilder.loadPerspective(Symbolizable.class, component);
 
     assertThat(perspective).isInstanceOf(Symbolizable.class);
-    assertThat(perspective.component()).isEqualTo(component);
+    assertThat(perspective.component().key()).isEqualTo(component.key());
+  }
+
+  @Test
+  public void project_should_not_be_highlightable() {
+    Component component = new ResourceComponent(new Project("struts").setEffectiveKey("org.struts"));
+
+    SymbolizableBuilder builder = new SymbolizableBuilder(mock(ResourceCache.class), mock(DefaultSensorStorage.class));
+    Perspective perspective = builder.loadPerspective(Symbolizable.class, component);
+
+    assertThat(perspective).isNull();
   }
 }
diff --git a/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java
deleted file mode 100644 (file)
index 0596157..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.batch.symbol;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.mockito.ArgumentCaptor;
-import org.sonar.api.source.Symbol;
-import org.sonar.batch.index.ComponentDataCache;
-import org.sonar.core.source.SnapshotDataTypes;
-
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.Set;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class DefaultSymbolTableBuilderTest {
-
-  @Rule
-  public ExpectedException throwable = ExpectedException.none();
-
-  @Test
-  public void should_write_symbol_and_references() throws Exception {
-    ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
-    DefaultSymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache);
-    Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20);
-    symbolTableBuilder.newReference(firstSymbol, 32);
-    Symbol secondSymbol = symbolTableBuilder.newSymbol(84, 92);
-    symbolTableBuilder.newReference(secondSymbol, 124);
-    Symbol thirdSymbol = symbolTableBuilder.newSymbol(55, 62);
-    symbolTableBuilder.newReference(thirdSymbol, 70);
-    symbolTableBuilder.done();
-
-    ArgumentCaptor<SymbolData> argCaptor = ArgumentCaptor.forClass(SymbolData.class);
-    verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture());
-
-    Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol = argCaptor.getValue().referencesBySymbol();
-
-    assertThat(new ArrayList<Symbol>(referencesBySymbol.keySet())).containsExactly(firstSymbol, secondSymbol, thirdSymbol);
-    assertThat(new ArrayList<Integer>(referencesBySymbol.get(firstSymbol))).containsExactly(32);
-    assertThat(new ArrayList<Integer>(referencesBySymbol.get(secondSymbol))).containsExactly(124);
-    assertThat(new ArrayList<Integer>(referencesBySymbol.get(thirdSymbol))).containsExactly(70);
-
-    assertThat(argCaptor.getValue().writeString()).isEqualTo("10,20,10,32;84,92,84,124;55,62,55,70");
-  }
-
-  @Test
-  public void should_serialize_unused_symbol() throws Exception {
-
-    ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
-    DefaultSymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache);
-    symbolTableBuilder.newSymbol(10, 20);
-    symbolTableBuilder.done();
-
-    ArgumentCaptor<SymbolData> argCaptor = ArgumentCaptor.forClass(SymbolData.class);
-    verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture());
-
-    assertThat(argCaptor.getValue().writeString()).isEqualTo("10,20,10");
-  }
-
-  @Test
-  public void should_reject_reference_conflicting_with_declaration() throws Exception {
-    throwable.expect(UnsupportedOperationException.class);
-
-    ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
-    DefaultSymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache);
-    Symbol symbol = symbolTableBuilder.newSymbol(10, 20);
-    symbolTableBuilder.newReference(symbol, 15);
-  }
-
-  @Test
-  public void should_reject_reference_from_another_file() throws Exception {
-    throwable.expect(UnsupportedOperationException.class);
-
-    ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
-    DefaultSymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache);
-    Symbol symbol = symbolTableBuilder.newSymbol(10, 20);
-
-    DefaultSymbolTableBuilder symbolTableBuilder2 = new DefaultSymbolTableBuilder("foo2", componentDataCache);
-    Symbol symbol2 = symbolTableBuilder2.newSymbol(30, 40);
-
-    symbolTableBuilder.newReference(symbol2, 15);
-  }
-
-}
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/scan/filesystem/glyphicons-halflings-regular.woff b/sonar-batch/src/test/resources/org/sonar/batch/scan/filesystem/glyphicons-halflings-regular.woff
deleted file mode 100644 (file)
index 2cc3e48..0000000
Binary files a/sonar-batch/src/test/resources/org/sonar/batch/scan/filesystem/glyphicons-halflings-regular.woff and /dev/null differ
index df2199c71032ab397fa1e6a81ae2184cefec499d..5668a840d697f4b79d2a97435ce15dcef7ce587b 100644 (file)
@@ -30,7 +30,7 @@ public abstract class GraphPerspectiveBuilder<T extends Perspective> extends Per
   protected final GraphPerspectiveLoader<T> perspectiveLoader;
 
   protected GraphPerspectiveBuilder(ScanGraph graph, Class<T> perspectiveClass, EdgePath path,
-                                    GraphPerspectiveLoader<T> perspectiveLoader) {
+    GraphPerspectiveLoader<T> perspectiveLoader) {
     super(perspectiveClass);
     this.graph = graph;
     this.path = path;
@@ -39,7 +39,7 @@ public abstract class GraphPerspectiveBuilder<T extends Perspective> extends Per
 
   public T create(ComponentVertex component) {
     return (T) component.beanGraph().createAdjacentVertex(component, perspectiveLoader.getBeanClass(),
-            perspectiveLoader.getPerspectiveKey());
+      perspectiveLoader.getPerspectiveKey());
   }
 
   public EdgePath path() {
@@ -51,7 +51,7 @@ public abstract class GraphPerspectiveBuilder<T extends Perspective> extends Per
   }
 
   @Override
-  protected T loadPerspective(Class<T> perspectiveClass, Component component) {
+  public T loadPerspective(Class<T> perspectiveClass, Component component) {
     ComponentVertex vertex;
     if (component instanceof ComponentVertex) {
       vertex = (ComponentVertex) component;
index 5b69ebaf05db83a6a9b6e1dcb10bcf6380a0499f..31f9c323d618b2b790be129f0b6cef53eae5e02f 100644 (file)
@@ -34,10 +34,10 @@ public abstract class PerspectiveBuilder<T extends Perspective> implements Batch
     this.perspectiveClass = perspectiveClass;
   }
 
-  protected Class<T> getPerspectiveClass() {
+  public Class<T> getPerspectiveClass() {
     return perspectiveClass;
   }
 
   @CheckForNull
-  protected abstract T loadPerspective(Class<T> perspectiveClass, Component component);
+  public abstract T loadPerspective(Class<T> perspectiveClass, Component component);
 }
diff --git a/sonar-core/src/main/java/org/sonar/core/component/ScanPerspectives.java b/sonar-core/src/main/java/org/sonar/core/component/ScanPerspectives.java
deleted file mode 100644 (file)
index 09cf4c7..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.core.component;
-
-import com.google.common.collect.Maps;
-import org.sonar.api.batch.SonarIndex;
-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.component.Component;
-import org.sonar.api.component.Perspective;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.resources.Directory;
-import org.sonar.api.resources.File;
-import org.sonar.api.resources.Resource;
-
-import javax.annotation.CheckForNull;
-import java.util.Map;
-
-public class ScanPerspectives implements ResourcePerspectives {
-
-  private final Map<Class<?>, PerspectiveBuilder<?>> builders = Maps.newHashMap();
-  private final SonarIndex resourceIndex;
-
-  public ScanPerspectives(PerspectiveBuilder[] builders, SonarIndex resourceIndex) {
-    this.resourceIndex = resourceIndex;
-    for (PerspectiveBuilder builder : builders) {
-      // TODO check duplications
-      this.builders.put(builder.getPerspectiveClass(), builder);
-    }
-  }
-
-  @Override
-  @CheckForNull
-  public <P extends Perspective> P as(Class<P> perspectiveClass, Component component) {
-    if (component.key() == null) {
-      return null;
-    }
-    PerspectiveBuilder<P> builder = builderFor(perspectiveClass);
-    return builder.loadPerspective(perspectiveClass, component);
-  }
-
-  @Override
-  @CheckForNull
-  public <P extends Perspective> P as(Class<P> perspectiveClass, Resource resource) {
-    Resource indexedResource = resource;
-    if (resource.getEffectiveKey() == null) {
-      indexedResource = resourceIndex.getResource(resource);
-    }
-    if (indexedResource != null) {
-      return as(perspectiveClass, new ResourceComponent(indexedResource));
-    }
-    return null;
-  }
-
-  @Override
-  public <P extends Perspective> P as(Class<P> perspectiveClass, InputPath inputPath) {
-    Resource r;
-    if (inputPath instanceof InputDir) {
-      r = Directory.create(((InputDir) inputPath).relativePath());
-    } else if (inputPath instanceof InputFile) {
-      r = File.create(((InputFile) inputPath).relativePath());
-    } else {
-      throw new IllegalArgumentException("Unknow input path type: " + inputPath);
-    }
-    return as(perspectiveClass, r);
-  }
-
-  private <T extends Perspective> PerspectiveBuilder<T> builderFor(Class<T> clazz) {
-    PerspectiveBuilder<T> builder = (PerspectiveBuilder<T>) builders.get(clazz);
-    if (builder == null) {
-      throw new PerspectiveNotFoundException("Perspective class is not registered: " + clazz);
-    }
-    return builder;
-  }
-}
index 4275f2c63d905092253c5291364cad2aea66a9b4..d3bb10520d55b3d47673a6f53040b63c669febad 100644 (file)
@@ -30,7 +30,7 @@ public class PerspectiveBuilderTest {
   public void testGetPerspectiveClass() throws Exception {
     PerspectiveBuilder<FakePerspective> builder = new PerspectiveBuilder<FakePerspective>(FakePerspective.class) {
       @Override
-      protected FakePerspective loadPerspective(Class<FakePerspective> perspectiveClass, Component component) {
+      public FakePerspective loadPerspective(Class<FakePerspective> perspectiveClass, Component component) {
         return null;
       }
     };
index 768986d8c0c915eb5e4861298378cf9d8d5f37bd..4e0af79ccc7233743d938a40adb0edd0312c234c 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.api.batch.fs;
 
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+
 import javax.annotation.CheckForNull;
 
 import java.io.File;
@@ -26,6 +28,14 @@ import java.nio.file.Path;
 
 /**
  * This layer over {@link java.io.File} adds information for code analyzers.
+ * For unit testing purpose you can create some {@link DefaultInputFile} and initialize
+ * all fields using 
+ * 
+ * <pre>
+ *   new DefaultInputFile("moduleKey", "relative/path/from/module/baseDir.java")
+ *     .setModuleBaseDir(path)
+ *     .initMetadata(new FileMetadata().readMetadata(someReader));
+ * </pre>
  *
  * @since 4.2
  */
@@ -100,4 +110,20 @@ public interface InputFile extends InputPath {
    */
   int lines();
 
+  /**
+   * Return a {@link TextPointer} in the given file.
+   * @param line Line of the pointer. Start at 1.
+   * @param lineOffset Offset in the line. Start at 0.
+   * @throw {@link IllegalArgumentException} if line or offset is not valid for the given file.
+   */
+  TextPointer newPointer(int line, int lineOffset);
+
+  /**
+   * Return a {@link TextRange} in the given file.
+   * @param start
+   * @param end
+   * @throw {@link IllegalArgumentException} if start or stop pointers are not valid for the given file.
+   */
+  TextRange newRange(TextPointer start, TextPointer end);
+
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextPointer.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextPointer.java
new file mode 100644 (file)
index 0000000..63f49b7
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+/**
+ * Represents a position in a text file {@link InputFile}
+ *
+ * @since 5.2
+ */
+public interface TextPointer extends Comparable<TextPointer> {
+
+  /**
+   * The logical line where this pointer is located. First line is 1.
+   */
+  int line();
+
+  /**
+   * The offset of this pointer in the current line. First position in a line is 0.
+   */
+  int lineOffset();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextRange.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextRange.java
new file mode 100644 (file)
index 0000000..08239a1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+/**
+ * Represents a text range in an {@link InputFile}
+ *
+ * @since 5.2
+ */
+public interface TextRange {
+
+  /**
+   * Start position of the range
+   */
+  TextPointer start();
+
+  /**
+   * End position of the range
+   */
+  TextPointer end();
+
+  /**
+   * Test if the current range has some common area with another range.
+   * Exemple: say the two ranges are on same line. Range with offsets [1,3] overlaps range with offsets [2,4] but not
+   * range with offset [3,5]
+   */
+  boolean overlap(TextRange another);
+
+}
index a24060a5df1bee8ba53ed663d4949bbe9ed1279d..83b4473fbea4fcdee03e1cfb3c159afd36bd1399 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
+import com.google.common.base.Preconditions;
 import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextPointer;
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.FileMetadata.Metadata;
 import org.sonar.api.utils.PathUtils;
 
 import javax.annotation.CheckForNull;
@@ -28,6 +32,7 @@ import javax.annotation.Nullable;
 import java.io.File;
 import java.nio.charset.Charset;
 import java.nio.file.Path;
+import java.util.Arrays;
 
 /**
  * @since 4.2
@@ -40,9 +45,13 @@ public class DefaultInputFile implements InputFile {
   private String language;
   private Type type = Type.MAIN;
   private Status status;
-  private int lines;
+  private int lines = -1;
   private Charset charset;
-  private int lastValidOffset;
+  private int lastValidOffset = -1;
+  private String hash;
+  private int nonBlankLines;
+  private int[] originalLineOffsets;
+  private boolean empty;
 
   public DefaultInputFile(String moduleKey, String relativePath) {
     this.moduleKey = moduleKey;
@@ -145,6 +154,7 @@ public class DefaultInputFile implements InputFile {
   }
 
   public int lastValidOffset() {
+    Preconditions.checkState(lastValidOffset >= 0, "InputFile is not properly initialized. Please set 'lastValidOffset' property.");
     return lastValidOffset;
   }
 
@@ -153,6 +163,108 @@ public class DefaultInputFile implements InputFile {
     return this;
   }
 
+  /**
+   * Digest hash of the file.
+   */
+  public String hash() {
+    return hash;
+  }
+
+  public int nonBlankLines() {
+    return nonBlankLines;
+  }
+
+  public int[] originalLineOffsets() {
+    Preconditions.checkState(originalLineOffsets != null, "InputFile is not properly initialized. Please set 'originalLineOffsets' property.");
+    Preconditions.checkState(originalLineOffsets.length == lines, "InputFile is not properly initialized. 'originalLineOffsets' property length should be equal to 'lines'");
+    return originalLineOffsets;
+  }
+
+  public DefaultInputFile setHash(String hash) {
+    this.hash = hash;
+    return this;
+  }
+
+  public DefaultInputFile setNonBlankLines(int nonBlankLines) {
+    this.nonBlankLines = nonBlankLines;
+    return this;
+  }
+
+  public DefaultInputFile setOriginalLineOffsets(int[] originalLineOffsets) {
+    this.originalLineOffsets = originalLineOffsets;
+    return this;
+  }
+
+  public boolean isEmpty() {
+    return this.empty;
+  }
+
+  public DefaultInputFile setEmpty(boolean empty) {
+    this.empty = empty;
+    return this;
+  }
+
+  @Override
+  public TextPointer newPointer(int line, int lineOffset) {
+    DefaultTextPointer textPointer = new DefaultTextPointer(line, lineOffset);
+    checkValid(textPointer, "pointer");
+    return textPointer;
+  }
+
+  private void checkValid(TextPointer pointer, String owner) {
+    Preconditions.checkArgument(pointer.line() >= 1, "%s is not a valid line for a file", pointer.line());
+    Preconditions.checkArgument(pointer.line() <= this.lines, "%s is not a valid line for %s. File %s has %s line(s)", pointer.line(), owner, this, lines);
+    Preconditions.checkArgument(pointer.lineOffset() >= 0, "%s is not a valid line offset for a file", pointer.lineOffset());
+    int lineLength = lineLength(pointer.line());
+    Preconditions.checkArgument(pointer.lineOffset() <= lineLength,
+      "%s is not a valid line offset for %s. File %s has %s character(s) at line %s", pointer.lineOffset(), owner, this, lineLength, pointer.line());
+  }
+
+  private int lineLength(int line) {
+    return lastValidGlobalOffsetForLine(line) - originalLineOffsets()[line - 1];
+  }
+
+  private int lastValidGlobalOffsetForLine(int line) {
+    return line < this.lines ? (originalLineOffsets()[line] - 1) : lastValidOffset();
+  }
+
+  @Override
+  public TextRange newRange(TextPointer start, TextPointer end) {
+    checkValid(start, "start pointer");
+    checkValid(end, "end pointer");
+    Preconditions.checkArgument(start.compareTo(end) < 0, "Start pointer %s should be before end pointer %s", start, end);
+    return new DefaultTextRange(start, end);
+  }
+
+  /**
+   * Create Range from global offsets. Used for backward compatibility with older API.
+   */
+  public TextRange newRange(int startOffset, int endOffset) {
+    return newRange(newPointer(startOffset), newPointer(endOffset));
+  }
+
+  public TextPointer newPointer(int globalOffset) {
+    Preconditions.checkArgument(globalOffset >= 0, "%s is not a valid offset for a file", globalOffset);
+    Preconditions.checkArgument(globalOffset <= lastValidOffset(), "%s is not a valid offset for file %s. Max offset is %s", globalOffset, this, lastValidOffset());
+    int line = findLine(globalOffset);
+    int startLineOffset = originalLineOffsets()[line - 1];
+    return new DefaultTextPointer(line, globalOffset - startLineOffset);
+  }
+
+  private int findLine(int globalOffset) {
+    return Math.abs(Arrays.binarySearch(originalLineOffsets(), globalOffset) + 1);
+  }
+
+  public DefaultInputFile initMetadata(Metadata metadata) {
+    this.setLines(metadata.lines);
+    this.setLastValidOffset(metadata.lastValidOffset);
+    this.setNonBlankLines(metadata.nonBlankLines);
+    this.setHash(metadata.hash);
+    this.setOriginalLineOffsets(metadata.originalLineOffsets);
+    this.setEmpty(metadata.empty);
+    return this;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextPointer.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextPointer.java
new file mode 100644 (file)
index 0000000..572aa4c
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.TextPointer;
+
+/**
+ * @since 5.2
+ */
+public class DefaultTextPointer implements TextPointer {
+
+  private final int line;
+  private final int lineOffset;
+
+  public DefaultTextPointer(int line, int lineOffset) {
+    this.line = line;
+    this.lineOffset = lineOffset;
+  }
+
+  @Override
+  public int line() {
+    return line;
+  }
+
+  @Override
+  public int lineOffset() {
+    return lineOffset;
+  }
+
+  @Override
+  public String toString() {
+    return "[line=" + line + ", lineOffset=" + lineOffset + "]";
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (!(obj instanceof DefaultTextPointer)) {
+      return false;
+    }
+    DefaultTextPointer other = (DefaultTextPointer) obj;
+    return other.line == this.line && other.lineOffset == this.lineOffset;
+  }
+
+  @Override
+  public int hashCode() {
+    return 37 * this.line + lineOffset;
+  }
+
+  @Override
+  public int compareTo(TextPointer o) {
+    if (this.line == o.line()) {
+      return Integer.compare(this.lineOffset, o.lineOffset());
+    }
+    return Integer.compare(this.line, o.line());
+  }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextRange.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextRange.java
new file mode 100644 (file)
index 0000000..b976d16
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.TextPointer;
+import org.sonar.api.batch.fs.TextRange;
+
+/**
+ * @since 5.2
+ */
+public class DefaultTextRange implements TextRange {
+
+  private final TextPointer start;
+  private final TextPointer end;
+
+  public DefaultTextRange(TextPointer start, TextPointer end) {
+    this.start = start;
+    this.end = end;
+  }
+
+  @Override
+  public TextPointer start() {
+    return start;
+  }
+
+  @Override
+  public TextPointer end() {
+    return end;
+  }
+
+  @Override
+  public boolean overlap(TextRange another) {
+    // [A,B] and [C,D]
+    // B > C && D > A
+    return this.end.compareTo(another.start()) > 0 && another.end().compareTo(this.start) > 0;
+  }
+
+  @Override
+  public String toString() {
+    return "Range[from " + start + " to " + end + "]";
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (!(obj instanceof DefaultTextRange)) {
+      return false;
+    }
+    DefaultTextRange other = (DefaultTextRange) obj;
+    return start.equals(other.start) && end.equals(other.end);
+  }
+
+  @Override
+  public int hashCode() {
+    return start.hashCode() * 17 + end.hashCode();
+  }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java
new file mode 100644 (file)
index 0000000..bb35754
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * 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 com.google.common.base.Charsets;
+import com.google.common.primitives.Ints;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.ByteOrderMark;
+import org.apache.commons.io.input.BOMInputStream;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Computes hash of files. Ends of Lines are ignored, so files with
+ * same content but different EOL encoding have the same hash.
+ */
+public class FileMetadata implements BatchComponent {
+
+  private static final Logger LOG = Loggers.get(FileMetadata.class);
+
+  private static final char LINE_FEED = '\n';
+  private static final char CARRIAGE_RETURN = '\r';
+
+  private abstract static class CharHandler {
+
+    void handleAll(char c) {
+    }
+
+    void handleIgnoreEoL(char c) {
+    }
+
+    void newLine() {
+    }
+
+    void eof() {
+    }
+  }
+
+  private static class LineCounter extends CharHandler {
+    private boolean empty = true;
+    private int lines = 1;
+    private int nonBlankLines = 0;
+    private boolean blankLine = true;
+    boolean alreadyLoggedInvalidCharacter = false;
+    private final File file;
+    private final Charset encoding;
+
+    LineCounter(File file, Charset encoding) {
+      this.file = file;
+      this.encoding = encoding;
+    }
+
+    @Override
+    void handleAll(char c) {
+      this.empty = false;
+      if (!alreadyLoggedInvalidCharacter && c == '\ufffd') {
+        LOG.warn("Invalid character encountered in file {} at line {} for encoding {}. Please fix file content or configure the encoding to be used using property '{}'.", file,
+          lines, encoding, CoreProperties.ENCODING_PROPERTY);
+        alreadyLoggedInvalidCharacter = true;
+      }
+    }
+
+    @Override
+    void newLine() {
+      lines++;
+      if (!blankLine) {
+        nonBlankLines++;
+      }
+      blankLine = true;
+    }
+
+    @Override
+    void handleIgnoreEoL(char c) {
+      if (!Character.isWhitespace(c)) {
+        blankLine = false;
+      }
+    }
+
+    @Override
+    void eof() {
+      if (!blankLine) {
+        nonBlankLines++;
+      }
+    }
+
+    public int lines() {
+      return lines;
+    }
+
+    public int nonBlankLines() {
+      return nonBlankLines;
+    }
+
+    public boolean isEmpty() {
+      return empty;
+    }
+  }
+
+  private static class FileHashComputer extends CharHandler {
+    private MessageDigest globalMd5Digest = DigestUtils.getMd5Digest();
+    private StringBuilder sb = new StringBuilder();
+
+    @Override
+    void handleIgnoreEoL(char c) {
+      sb.append(c);
+    }
+
+    @Override
+    void newLine() {
+      sb.append(LINE_FEED);
+      globalMd5Digest.update(sb.toString().getBytes(Charsets.UTF_8));
+      sb.setLength(0);
+    }
+
+    @Override
+    void eof() {
+      if (sb.length() > 0) {
+        globalMd5Digest.update(sb.toString().getBytes(Charsets.UTF_8));
+      }
+    }
+
+    @CheckForNull
+    public String getHash() {
+      return Hex.encodeHexString(globalMd5Digest.digest());
+    }
+  }
+
+  private static class LineHashComputer extends CharHandler {
+    private final MessageDigest lineMd5Digest = DigestUtils.getMd5Digest();
+    private final StringBuilder sb = new StringBuilder();
+    private final LineHashConsumer consumer;
+    private int line = 1;
+
+    public LineHashComputer(LineHashConsumer consumer) {
+      this.consumer = consumer;
+    }
+
+    @Override
+    void handleIgnoreEoL(char c) {
+      if (!Character.isWhitespace(c)) {
+        sb.append(c);
+      }
+    }
+
+    @Override
+    void newLine() {
+      consumer.consume(line, sb.length() > 0 ? lineMd5Digest.digest(sb.toString().getBytes(Charsets.UTF_8)) : null);
+      sb.setLength(0);
+      line++;
+    }
+
+    @Override
+    void eof() {
+      consumer.consume(line, sb.length() > 0 ? lineMd5Digest.digest(sb.toString().getBytes(Charsets.UTF_8)) : null);
+    }
+
+  }
+
+  private static class LineOffsetCounter extends CharHandler {
+    private int currentOriginalOffset = 0;
+    private List<Integer> originalLineOffsets = new ArrayList<Integer>();
+    private int lastValidOffset = 0;
+
+    public LineOffsetCounter() {
+      originalLineOffsets.add(0);
+    }
+
+    @Override
+    void handleAll(char c) {
+      currentOriginalOffset++;
+    }
+
+    @Override
+    void newLine() {
+      originalLineOffsets.add(currentOriginalOffset);
+    }
+
+    @Override
+    void eof() {
+      lastValidOffset = currentOriginalOffset;
+    }
+
+    public List<Integer> getOriginalLineOffsets() {
+      return originalLineOffsets;
+    }
+
+    public int getLastValidOffset() {
+      return lastValidOffset;
+    }
+
+  }
+
+  /**
+   * Compute hash of a file ignoring line ends differences.
+   * Maximum performance is needed.
+   */
+  public Metadata readMetadata(File file, Charset encoding) {
+    LineCounter lineCounter = new LineCounter(file, encoding);
+    FileHashComputer fileHashComputer = new FileHashComputer();
+    LineOffsetCounter lineOffsetCounter = new LineOffsetCounter();
+    readFile(file, encoding, lineCounter, fileHashComputer, lineOffsetCounter);
+    return new Metadata(lineCounter.lines(), lineCounter.nonBlankLines(), fileHashComputer.getHash(), lineOffsetCounter.getOriginalLineOffsets(),
+      lineOffsetCounter.getLastValidOffset(),
+      lineCounter.isEmpty());
+  }
+
+  /**
+   * For testing purpose
+   */
+  public Metadata readMetadata(Reader reader) {
+    LineCounter lineCounter = new LineCounter(new File("fromString"), Charsets.UTF_16);
+    FileHashComputer fileHashComputer = new FileHashComputer();
+    LineOffsetCounter lineOffsetCounter = new LineOffsetCounter();
+    try {
+      read(reader, lineCounter, fileHashComputer, lineOffsetCounter);
+    } catch (IOException e) {
+      throw new IllegalStateException("Should never occurs", e);
+    }
+    return new Metadata(lineCounter.lines(), lineCounter.nonBlankLines(), fileHashComputer.getHash(), lineOffsetCounter.getOriginalLineOffsets(),
+      lineOffsetCounter.getLastValidOffset(),
+      lineCounter.isEmpty());
+  }
+
+  private static void readFile(File file, Charset encoding, CharHandler... handlers) {
+    try (BOMInputStream bomIn = new BOMInputStream(new FileInputStream(file),
+      ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE);
+      Reader reader = new BufferedReader(new InputStreamReader(bomIn, encoding))) {
+      read(reader, handlers);
+    } catch (IOException e) {
+      throw new IllegalStateException(String.format("Fail to read file '%s' with encoding '%s'", file.getAbsolutePath(), encoding), e);
+    }
+  }
+
+  private static void read(Reader reader, CharHandler... handlers) throws IOException {
+    char c = (char) 0;
+    int i = reader.read();
+    boolean afterCR = false;
+    while (i != -1) {
+      c = (char) i;
+      if (afterCR) {
+        for (CharHandler handler : handlers) {
+          if (c != CARRIAGE_RETURN && c != LINE_FEED) {
+            handler.handleIgnoreEoL(c);
+          }
+          handler.handleAll(c);
+          handler.newLine();
+        }
+        afterCR = c == CARRIAGE_RETURN;
+      } else if (c == LINE_FEED) {
+        for (CharHandler handler : handlers) {
+          handler.handleAll(c);
+          handler.newLine();
+        }
+      } else if (c == CARRIAGE_RETURN) {
+        afterCR = true;
+        for (CharHandler handler : handlers) {
+          handler.handleAll(c);
+        }
+      } else {
+        for (CharHandler handler : handlers) {
+          handler.handleIgnoreEoL(c);
+          handler.handleAll(c);
+        }
+      }
+      i = reader.read();
+    }
+    for (CharHandler handler : handlers) {
+      handler.eof();
+    }
+  }
+
+  public static class Metadata {
+    final int lines;
+    final int nonBlankLines;
+    final String hash;
+    final int[] originalLineOffsets;
+    final int lastValidOffset;
+    final boolean empty;
+
+    private Metadata(int lines, int nonBlankLines, String hash, List<Integer> originalLineOffsets, int lastValidOffset, boolean empty) {
+      this.lines = lines;
+      this.nonBlankLines = nonBlankLines;
+      this.hash = hash;
+      this.empty = empty;
+      this.originalLineOffsets = Ints.toArray(originalLineOffsets);
+      this.lastValidOffset = lastValidOffset;
+    }
+  }
+
+  public static interface LineHashConsumer {
+
+    void consume(int lineIdx, @Nullable byte[] hash);
+
+  }
+
+  /**
+   * Compute a MD5 hash of each line of the file after removing of all blank chars
+   */
+  public static void computeLineHashesForIssueTracking(DefaultInputFile f, LineHashConsumer consumer) {
+    readFile(f.file(), f.charset(), new LineHashComputer(consumer));
+  }
+}
index 60e0db9a2d3ab3c845dc7ac2ab6cf2ad6defad80..50bb7cbab3098c1ea7976bcb05af1897f54d90ba 100644 (file)
@@ -36,7 +36,7 @@ import java.util.Set;
 
 public class DefaultHighlighting extends DefaultStorable implements NewHighlighting {
 
-  private InputFile inputFile;
+  private DefaultInputFile inputFile;
   private Set<SyntaxHighlightingRule> syntaxHighlightingRuleSet;
 
   public DefaultHighlighting() {
@@ -48,9 +48,9 @@ public class DefaultHighlighting extends DefaultStorable implements NewHighlight
     syntaxHighlightingRuleSet = Sets.newTreeSet(new Comparator<SyntaxHighlightingRule>() {
       @Override
       public int compare(SyntaxHighlightingRule left, SyntaxHighlightingRule right) {
-        int result = left.getStartPosition() - right.getStartPosition();
+        int result = left.range().start().compareTo(right.range().start());
         if (result == 0) {
-          result = right.getEndPosition() - left.getEndPosition();
+          result = right.range().end().compareTo(left.range().end());
         }
         return result;
       }
@@ -67,9 +67,9 @@ public class DefaultHighlighting extends DefaultStorable implements NewHighlight
       SyntaxHighlightingRule previous = it.next();
       while (it.hasNext()) {
         SyntaxHighlightingRule current = it.next();
-        if (previous.getEndPosition() > current.getStartPosition() && !(previous.getEndPosition() >= current.getEndPosition())) {
-          String errorMsg = String.format("Cannot register highlighting rule for characters from %s to %s as it " +
-            "overlaps at least one existing rule", current.getStartPosition(), current.getEndPosition());
+        if (previous.range().end().compareTo(current.range().start()) > 0 && !(previous.range().end().compareTo(current.range().end()) >= 0)) {
+          String errorMsg = String.format("Cannot register highlighting rule for characters at %s as it " +
+            "overlaps at least one existing rule", current.range());
           throw new IllegalStateException(errorMsg);
         }
         previous = current;
@@ -80,7 +80,7 @@ public class DefaultHighlighting extends DefaultStorable implements NewHighlight
   @Override
   public DefaultHighlighting onFile(InputFile inputFile) {
     Preconditions.checkNotNull(inputFile, "file can't be null");
-    this.inputFile = inputFile;
+    this.inputFile = (DefaultInputFile) inputFile;
     return this;
   }
 
@@ -91,21 +91,11 @@ public class DefaultHighlighting extends DefaultStorable implements NewHighlight
   @Override
   public DefaultHighlighting highlight(int startOffset, int endOffset, TypeOfText typeOfText) {
     Preconditions.checkState(inputFile != null, "Call onFile() first");
-    int maxValidOffset = ((DefaultInputFile) inputFile).lastValidOffset();
-    checkOffset(startOffset, maxValidOffset, "startOffset");
-    checkOffset(endOffset, maxValidOffset, "endOffset");
-    Preconditions.checkArgument(startOffset < endOffset, "startOffset (" + startOffset + ") should be < endOffset (" + endOffset + ") for file " + inputFile + ".");
-    SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(startOffset, endOffset,
-      typeOfText);
+    SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(inputFile.newRange(startOffset, endOffset), typeOfText);
     this.syntaxHighlightingRuleSet.add(syntaxHighlightingRule);
     return this;
   }
 
-  private void checkOffset(int offset, int maxValidOffset, String label) {
-    Preconditions.checkArgument(offset >= 0 && offset <= maxValidOffset, "Invalid " + label + " " + offset + ". Should be >= 0 and <= " + maxValidOffset
-      + " for file " + inputFile);
-  }
-
   @Override
   protected void doSave() {
     Preconditions.checkState(inputFile != null, "Call onFile() first");
index 9989449ff000a66181a317b1dc51374ccff171a6..c9b1f000c092cd396e81cff9a1d5192d0a7789a2 100644 (file)
  */
 package org.sonar.api.batch.sensor.highlighting.internal;
 
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.batch.fs.TextRange;
 import org.sonar.api.batch.sensor.highlighting.TypeOfText;
 
-import java.io.Serializable;
+public class SyntaxHighlightingRule {
 
-public class SyntaxHighlightingRule implements Serializable {
-
-  private final int startPosition;
-  private final int endPosition;
+  private final TextRange range;
   private final TypeOfText textType;
 
-  private SyntaxHighlightingRule(int startPosition, int endPosition, TypeOfText textType) {
-    this.startPosition = startPosition;
-    this.endPosition = endPosition;
+  private SyntaxHighlightingRule(TextRange range, TypeOfText textType) {
+    this.range = range;
     this.textType = textType;
   }
 
-  public static SyntaxHighlightingRule create(int startPosition, int endPosition, TypeOfText textType) {
-    return new SyntaxHighlightingRule(startPosition, endPosition, textType);
-  }
-
-  public int getStartPosition() {
-    return startPosition;
+  public static SyntaxHighlightingRule create(TextRange range, TypeOfText textType) {
+    return new SyntaxHighlightingRule(range, textType);
   }
 
-  public int getEndPosition() {
-    return endPosition;
+  public TextRange range() {
+    return range;
   }
 
   public TypeOfText getTextType() {
@@ -53,6 +48,6 @@ public class SyntaxHighlightingRule implements Serializable {
 
   @Override
   public String toString() {
-    return "" + startPosition + "," + endPosition + "," + textType.cssClass();
+    return ReflectionToStringBuilder.toString(this, ToStringStyle.SIMPLE_STYLE);
   }
 }
index 2846e0676fb3bba29ddf413b27b0539e0ee25480..278d9ad174167817ff3ae143d5a9408738cbb621 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.api.batch.fs.InputPath;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputDir;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultTextPointer;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
 import org.sonar.api.batch.sensor.Sensor;
@@ -55,12 +56,7 @@ import javax.annotation.Nullable;
 
 import java.io.File;
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * Utility class to help testing {@link Sensor}.
@@ -179,14 +175,15 @@ public class SensorContextTester implements SensorContext {
     return new DefaultHighlighting(sensorStorage);
   }
 
-  public List<TypeOfText> highlightingTypeFor(String componentKey, int charIndex) {
+  public List<TypeOfText> highlightingTypeAt(String componentKey, int line, int lineOffset) {
     DefaultHighlighting syntaxHighlightingData = sensorStorage.highlightingByComponent.get(componentKey);
     if (syntaxHighlightingData == null) {
       return Collections.emptyList();
     }
     List<TypeOfText> result = new ArrayList<TypeOfText>();
+    DefaultTextPointer location = new DefaultTextPointer(line, lineOffset);
     for (SyntaxHighlightingRule sortedRule : syntaxHighlightingData.getSyntaxHighlightingRuleSet()) {
-      if (sortedRule.getStartPosition() <= charIndex && sortedRule.getEndPosition() > charIndex) {
+      if (sortedRule.range().start().compareTo(location) <= 0 && sortedRule.range().end().compareTo(location) > 0) {
         result.add(sortedRule.getTextType());
       }
     }
index 850d3e6cdc7288022f1d8fbff25de6407fa030fd..121ba2a874c5c3ad2ede2cf175e6392d0b3d0efe 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.api.batch.sensor.internal;
 
+import org.sonar.api.BatchComponent;
 import org.sonar.api.batch.sensor.dependency.Dependency;
 import org.sonar.api.batch.sensor.duplication.Duplication;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
@@ -29,7 +30,7 @@ import org.sonar.api.batch.sensor.measure.Measure;
  * Interface for storing data computed by sensors.
  * @since 5.1
  */
-public interface SensorStorage {
+public interface SensorStorage extends BatchComponent {
 
   void store(Measure measure);
 
index a5f7773e745706d0b965d63e78e90eee98a9689c..8f16849892e46b1bd209bcfc7a78901cdee9fd21 100644 (file)
@@ -22,6 +22,10 @@ package org.sonar.api.component;
 import org.sonar.api.BatchComponent;
 import org.sonar.api.ServerComponent;
 
+/**
+ * @deprecated since 5.2 unused
+ */
+@Deprecated
 public interface Perspectives extends BatchComponent, ServerComponent {
 
   <P extends Perspective> P as(Class<P> perspectiveClass, Component component);
index d92592678eb98851dd5d58ae59dcfccea8a50497..e8555ed4e35dae082a0309cbce930be4d5bb3bde 100644 (file)
@@ -32,6 +32,7 @@ import org.sonar.api.ServerComponent;
 import org.sonar.api.config.PropertyDefinitions;
 
 import javax.annotation.Nullable;
+
 import java.util.Collection;
 import java.util.List;
 
@@ -172,7 +173,11 @@ public class ComponentContainer implements BatchComponent, ServerComponent {
     if (component instanceof ComponentAdapter) {
       pico.addAdapter((ComponentAdapter) component);
     } else {
-      pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component);
+      try {
+        pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component);
+      } catch (Throwable t) {
+        throw new IllegalStateException("Unable to register component " + getName(component), t);
+      }
       declareExtension(null, component);
     }
     return this;
index dd4c7d408835bb18dd236f356436fad053b0c3d4..091b881fabec25765bcc5d7fd328b1621513a874 100644 (file)
@@ -22,12 +22,19 @@ package org.sonar.api.source;
 
 public interface Symbol {
 
+  /**
+   * @deprecated in 5.2 not used.
+   */
+  @Deprecated
   int getDeclarationStartOffset();
 
+  /**
+   * @deprecated in 5.2 not used.
+   */
+  @Deprecated
   int getDeclarationEndOffset();
 
   /**
-   * @since unused
    * @deprecated in 4.3 not used.
    */
   @Deprecated
index 1cc821620478db52616273848eed105783aeaa03..f205ec41c17fa8e24c214925e3363c467a49a12c 100644 (file)
@@ -44,6 +44,10 @@ public interface Symbolizable extends Perspective {
 
     List<Symbol> symbols();
 
+    /**
+     * @deprecated since 5.2 not used
+     */
+    @Deprecated
     List<Integer> references(Symbol symbol);
   }
 
index edf072dca79404c5bfbaab88ab1bf004fcab6804..6b8b6af9fea8fc1631df9885cfed87ffca32d115 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.api.batch.fs.InputFile;
 import java.io.File;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
 
 public class DefaultInputFileTest {
 
@@ -73,4 +74,110 @@ public class DefaultInputFileTest {
     DefaultInputFile file = new DefaultInputFile("ABCDE", "src/Foo.php");
     assertThat(file.toString()).isEqualTo("[moduleKey=ABCDE, relative=src/Foo.php, basedir=null]");
   }
+
+  @Test
+  public void checkValidPointer() {
+    DefaultInputFile file = new DefaultInputFile("ABCDE", "src/Foo.php");
+    file.setLines(2);
+    file.setOriginalLineOffsets(new int[] {0, 10});
+    file.setLastValidOffset(15);
+    assertThat(file.newPointer(1, 0).line()).isEqualTo(1);
+    assertThat(file.newPointer(1, 0).lineOffset()).isEqualTo(0);
+    // Don't fail
+    file.newPointer(1, 9);
+    file.newPointer(2, 0);
+    file.newPointer(2, 5);
+
+    try {
+      file.newPointer(0, 1);
+      fail();
+    } catch (Exception e) {
+      assertThat(e).hasMessage("0 is not a valid line for a file");
+    }
+    try {
+      file.newPointer(3, 1);
+      fail();
+    } catch (Exception e) {
+      assertThat(e).hasMessage("3 is not a valid line for pointer. File [moduleKey=ABCDE, relative=src/Foo.php, basedir=null] has 2 line(s)");
+    }
+    try {
+      file.newPointer(1, -1);
+      fail();
+    } catch (Exception e) {
+      assertThat(e).hasMessage("-1 is not a valid line offset for a file");
+    }
+    try {
+      file.newPointer(1, 10);
+      fail();
+    } catch (Exception e) {
+      assertThat(e).hasMessage("10 is not a valid line offset for pointer. File [moduleKey=ABCDE, relative=src/Foo.php, basedir=null] has 9 character(s) at line 1");
+    }
+  }
+
+  @Test
+  public void checkValidPointerUsingGlobalOffset() {
+    DefaultInputFile file = new DefaultInputFile("ABCDE", "src/Foo.php");
+    file.setLines(2);
+    file.setOriginalLineOffsets(new int[] {0, 10});
+    file.setLastValidOffset(15);
+    assertThat(file.newPointer(0).line()).isEqualTo(1);
+    assertThat(file.newPointer(0).lineOffset()).isEqualTo(0);
+
+    assertThat(file.newPointer(9).line()).isEqualTo(1);
+    assertThat(file.newPointer(9).lineOffset()).isEqualTo(9);
+
+    assertThat(file.newPointer(10).line()).isEqualTo(2);
+    assertThat(file.newPointer(10).lineOffset()).isEqualTo(0);
+
+    assertThat(file.newPointer(15).line()).isEqualTo(2);
+    assertThat(file.newPointer(15).lineOffset()).isEqualTo(5);
+
+    try {
+      file.newPointer(-1);
+      fail();
+    } catch (Exception e) {
+      assertThat(e).hasMessage("-1 is not a valid offset for a file");
+    }
+
+    try {
+      file.newPointer(16);
+      fail();
+    } catch (Exception e) {
+      assertThat(e).hasMessage("16 is not a valid offset for file [moduleKey=ABCDE, relative=src/Foo.php, basedir=null]. Max offset is 15");
+    }
+  }
+
+  @Test
+  public void checkValidRange() {
+    DefaultInputFile file = new DefaultInputFile("ABCDE", "src/Foo.php");
+    file.setLines(2);
+    file.setOriginalLineOffsets(new int[] {0, 10});
+    file.setLastValidOffset(15);
+    assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(2, 1)).start().line()).isEqualTo(1);
+    // Don't fail
+    file.newRange(file.newPointer(1, 0), file.newPointer(1, 1));
+    file.newRange(file.newPointer(1, 0), file.newPointer(1, 9));
+    file.newRange(file.newPointer(1, 0), file.newPointer(2, 0));
+    assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(2, 5))).isEqualTo(file.newRange(0, 15));
+
+    try {
+      file.newRange(file.newPointer(1, 0), file.newPointer(1, 0));
+      fail();
+    } catch (Exception e) {
+      assertThat(e).hasMessage("Start pointer [line=1, lineOffset=0] should be before end pointer [line=1, lineOffset=0]");
+    }
+  }
+
+  @Test
+  public void testRangeOverlap() {
+    DefaultInputFile file = new DefaultInputFile("ABCDE", "src/Foo.php");
+    file.setLines(2);
+    file.setOriginalLineOffsets(new int[] {0, 10});
+    file.setLastValidOffset(15);
+    // Don't fail
+    assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)).overlap(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)))).isTrue();
+    assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)).overlap(file.newRange(file.newPointer(1, 0), file.newPointer(1, 2)))).isTrue();
+    assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)).overlap(file.newRange(file.newPointer(1, 1), file.newPointer(1, 2)))).isFalse();
+    assertThat(file.newRange(file.newPointer(1, 2), file.newPointer(1, 3)).overlap(file.newRange(file.newPointer(1, 0), file.newPointer(1, 2)))).isFalse();
+  }
 }
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FileMetadataTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FileMetadataTest.java
new file mode 100644 (file)
index 0000000..7696c1a
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * 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 com.google.common.base.Charsets;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.io.FileUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.FileMetadata.LineHashConsumer;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+
+import javax.annotation.Nullable;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FileMetadataTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Rule
+  public LogTester logTester = new LogTester();
+
+  @Test
+  public void empty_file() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.touch(tempFile);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(1);
+    assertThat(metadata.nonBlankLines).isEqualTo(0);
+    assertThat(metadata.hash).isNotEmpty();
+    assertThat(metadata.originalLineOffsets).containsOnly(0);
+    assertThat(metadata.lastValidOffset).isEqualTo(0);
+    assertThat(metadata.empty).isTrue();
+  }
+
+  @Test
+  public void windows_without_latest_eol() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "foo\r\nbar\r\nbaz", Charsets.UTF_8, true);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(3);
+    assertThat(metadata.nonBlankLines).isEqualTo(3);
+    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 5, 10);
+    assertThat(metadata.lastValidOffset).isEqualTo(13);
+    assertThat(metadata.empty).isFalse();
+  }
+
+  @Test
+  public void read_with_wrong_encoding() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "marker´s\n", Charset.forName("cp1252"));
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(2);
+    assertThat(metadata.hash).isEqualTo(md5Hex("marker\ufffds\n"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 9);
+  }
+
+  @Test
+  public void non_ascii_utf_8() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "föo\r\nbàr\r\n\u1D11Ebaßz\r\n", Charsets.UTF_8, true);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(4);
+    assertThat(metadata.nonBlankLines).isEqualTo(3);
+    assertThat(metadata.hash).isEqualTo(md5Hex("föo\nbàr\n\u1D11Ebaßz\n"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 5, 10, 18);
+  }
+
+  @Test
+  public void non_ascii_utf_16() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "föo\r\nbàr\r\n\u1D11Ebaßz\r\n", Charsets.UTF_16, true);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_16);
+    assertThat(metadata.lines).isEqualTo(4);
+    assertThat(metadata.nonBlankLines).isEqualTo(3);
+    assertThat(metadata.hash).isEqualTo(md5Hex("föo\nbàr\n\u1D11Ebaßz\n"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 5, 10, 18);
+  }
+
+  @Test
+  public void unix_without_latest_eol() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "foo\nbar\nbaz", Charsets.UTF_8, true);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(3);
+    assertThat(metadata.nonBlankLines).isEqualTo(3);
+    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 8);
+    assertThat(metadata.lastValidOffset).isEqualTo(11);
+  }
+
+  @Test
+  public void unix_with_latest_eol() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "foo\nbar\nbaz\n", Charsets.UTF_8, true);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(4);
+    assertThat(metadata.nonBlankLines).isEqualTo(3);
+    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz\n"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 8, 12);
+    assertThat(metadata.lastValidOffset).isEqualTo(12);
+  }
+
+  @Test
+  public void mix_of_newlines_with_latest_eol() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "foo\nbar\r\nbaz\n", Charsets.UTF_8, true);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(4);
+    assertThat(metadata.nonBlankLines).isEqualTo(3);
+    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz\n"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 9, 13);
+  }
+
+  @Test
+  public void several_new_lines() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "foo\n\n\nbar", Charsets.UTF_8, true);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(4);
+    assertThat(metadata.nonBlankLines).isEqualTo(2);
+    assertThat(metadata.hash).isEqualTo(md5Hex("foo\n\n\nbar"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 5, 6);
+  }
+
+  @Test
+  public void mix_of_newlines_without_latest_eol() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "foo\nbar\r\nbaz", Charsets.UTF_8, true);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(3);
+    assertThat(metadata.nonBlankLines).isEqualTo(3);
+    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 9);
+  }
+
+  @Test
+  public void start_with_newline() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "\nfoo\nbar\r\nbaz", Charsets.UTF_8, true);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(4);
+    assertThat(metadata.nonBlankLines).isEqualTo(3);
+    assertThat(metadata.hash).isEqualTo(md5Hex("\nfoo\nbar\nbaz"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 1, 5, 10);
+  }
+
+  @Test
+  public void start_with_bom() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, "\uFEFFfoo\nbar\r\nbaz", Charsets.UTF_8, true);
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(3);
+    assertThat(metadata.nonBlankLines).isEqualTo(3);
+    assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz"));
+    assertThat(metadata.originalLineOffsets).containsOnly(0, 4, 9);
+  }
+
+  @Test
+  public void ignore_whitespace_when_computing_line_hashes() throws Exception {
+    File tempFile = temp.newFile();
+    FileUtils.write(tempFile, " foo\nb ar\r\nbaz \t", Charsets.UTF_8, true);
+
+    DefaultInputFile f = new DefaultInputFile("foo", tempFile.getName());
+    f.setModuleBaseDir(tempFile.getParentFile().toPath());
+    f.setCharset(Charsets.UTF_8);
+    FileMetadata.computeLineHashesForIssueTracking(f, new LineHashConsumer() {
+
+      @Override
+      public void consume(int lineIdx, @Nullable byte[] hash) {
+        switch (lineIdx) {
+          case 1:
+            assertThat(Hex.encodeHexString(hash)).isEqualTo(md5Hex("foo"));
+            break;
+          case 2:
+            assertThat(Hex.encodeHexString(hash)).isEqualTo(md5Hex("bar"));
+            break;
+          case 3:
+            assertThat(Hex.encodeHexString(hash)).isEqualTo(md5Hex("baz"));
+            break;
+        }
+      }
+    });
+  }
+
+  @Test
+  public void should_throw_if_file_does_not_exist() throws Exception {
+    File tempFolder = temp.newFolder();
+    File file = new File(tempFolder, "doesNotExist.txt");
+
+    thrown.expect(IllegalStateException.class);
+    thrown.expectMessage("Fail to read file '" + file.getAbsolutePath() + "' with encoding 'UTF-8'");
+
+    new FileMetadata().readMetadata(file, Charsets.UTF_8);
+  }
+
+  @Test
+  public void line_feed_is_included_into_hash() throws Exception {
+    File file1 = temp.newFile();
+    FileUtils.write(file1, "foo\nbar\n", Charsets.UTF_8, true);
+
+    // same as file1, except an additional return carriage
+    File file1a = temp.newFile();
+    FileUtils.write(file1a, "foo\r\nbar\n", Charsets.UTF_8, true);
+
+    File file2 = temp.newFile();
+    FileUtils.write(file2, "foo\nbar", Charsets.UTF_8, true);
+
+    String hash1 = new FileMetadata().readMetadata(file1, Charsets.UTF_8).hash;
+    String hash1a = new FileMetadata().readMetadata(file1a, Charsets.UTF_8).hash;
+    String hash2 = new FileMetadata().readMetadata(file2, Charsets.UTF_8).hash;
+    assertThat(hash1).isEqualTo(hash1a);
+    assertThat(hash1).isNotEqualTo(hash2);
+  }
+
+  @Test
+  public void binary_file_with_unmappable_character() throws Exception {
+    File woff = new File(this.getClass().getResource("glyphicons-halflings-regular.woff").toURI());
+
+    FileMetadata.Metadata metadata = new FileMetadata().readMetadata(woff, Charsets.UTF_8);
+    assertThat(metadata.lines).isEqualTo(135);
+    assertThat(metadata.nonBlankLines).isEqualTo(134);
+    assertThat(metadata.hash).isNotEmpty();
+    assertThat(metadata.empty).isFalse();
+
+    assertThat(logTester.logs(LoggerLevel.WARN).get(0)).contains("Invalid character encountered in file");
+    assertThat(logTester.logs(LoggerLevel.WARN).get(0)).contains(
+      "glyphicons-halflings-regular.woff at line 1 for encoding UTF-8. Please fix file content or configure the encoding to be used using property 'sonar.sourceEncoding'.");
+  }
+
+}
index 4cf1225e27bf157daba6676cc0c04f4966a39348..79f7db9e06b92c8d548c1d274e3b11fa6cd3e52f 100644 (file)
@@ -23,7 +23,10 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.fs.TextRange;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultTextPointer;
+import org.sonar.api.batch.fs.internal.DefaultTextRange;
 import org.sonar.api.batch.sensor.internal.SensorStorage;
 
 import java.util.Collection;
@@ -36,6 +39,11 @@ import static org.sonar.api.batch.sensor.highlighting.TypeOfText.KEYWORD;
 
 public class DefaultHighlightingTest {
 
+  private static final DefaultInputFile INPUT_FILE = new DefaultInputFile("foo", "src/Foo.java")
+    .setLines(2)
+    .setOriginalLineOffsets(new int[] {0, 50})
+    .setLastValidOffset(100);
+
   private Collection<SyntaxHighlightingRule> highlightingRules;
 
   @Rule
@@ -45,7 +53,7 @@ public class DefaultHighlightingTest {
   public void setUpSampleRules() {
 
     DefaultHighlighting highlightingDataBuilder = new DefaultHighlighting()
-      .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(100))
+      .onFile(INPUT_FILE)
       .highlight(0, 10, COMMENT)
       .highlight(10, 12, KEYWORD)
       .highlight(24, 38, KEYWORD)
@@ -61,17 +69,25 @@ public class DefaultHighlightingTest {
     assertThat(highlightingRules).hasSize(6);
   }
 
+  private static TextRange rangeOf(int startLine, int startOffset, int endLine, int endOffset) {
+    return new DefaultTextRange(new DefaultTextPointer(startLine, startOffset), new DefaultTextPointer(endLine, endOffset));
+  }
+
   @Test
   public void should_order_by_start_then_end_offset() throws Exception {
-    assertThat(highlightingRules).extracting("startPosition").containsOnly(0, 10, 12, 24, 24, 42);
-    assertThat(highlightingRules).extracting("endPosition").containsOnly(10, 12, 20, 38, 65, 50);
+    assertThat(highlightingRules).extracting("range", TextRange.class).containsExactly(rangeOf(1, 0, 1, 10),
+      rangeOf(1, 10, 1, 12),
+      rangeOf(1, 12, 1, 20),
+      rangeOf(1, 24, 2, 15),
+      rangeOf(1, 24, 1, 38),
+      rangeOf(1, 42, 2, 0));
     assertThat(highlightingRules).extracting("textType").containsOnly(COMMENT, KEYWORD, COMMENT, KEYWORD, CPP_DOC, KEYWORD);
   }
 
   @Test
   public void should_suport_overlapping() throws Exception {
     new DefaultHighlighting(mock(SensorStorage.class))
-      .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(100))
+      .onFile(INPUT_FILE)
       .highlight(0, 15, KEYWORD)
       .highlight(8, 12, CPP_DOC)
       .save();
@@ -80,49 +96,14 @@ public class DefaultHighlightingTest {
   @Test
   public void should_prevent_boudaries_overlapping() throws Exception {
     throwable.expect(IllegalStateException.class);
-    throwable.expectMessage("Cannot register highlighting rule for characters from 8 to 15 as it overlaps at least one existing rule");
+    throwable
+      .expectMessage("Cannot register highlighting rule for characters at Range[from [line=1, lineOffset=8] to [line=1, lineOffset=15]] as it overlaps at least one existing rule");
 
     new DefaultHighlighting(mock(SensorStorage.class))
-      .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(100))
+      .onFile(INPUT_FILE)
       .highlight(0, 10, KEYWORD)
       .highlight(8, 15, KEYWORD)
       .save();
   }
 
-  @Test
-  public void should_prevent_invalid_offset() throws Exception {
-    throwable.expect(IllegalArgumentException.class);
-    throwable.expectMessage("Invalid endOffset 15. Should be >= 0 and <= 10 for file [moduleKey=foo, relative=src/Foo.java, basedir=null]");
-
-    new DefaultHighlighting(mock(SensorStorage.class))
-      .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(10))
-      .highlight(0, 10, KEYWORD)
-      .highlight(8, 15, KEYWORD)
-      .save();
-  }
-
-  @Test
-  public void positive_offset() throws Exception {
-    throwable.expect(IllegalArgumentException.class);
-    throwable.expectMessage("Invalid startOffset -8. Should be >= 0 and <= 10 for file [moduleKey=foo, relative=src/Foo.java, basedir=null]");
-
-    new DefaultHighlighting(mock(SensorStorage.class))
-      .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(10))
-      .highlight(0, 10, KEYWORD)
-      .highlight(-8, 15, KEYWORD)
-      .save();
-  }
-
-  @Test
-  public void should_prevent_invalid_offset_order() throws Exception {
-    throwable.expect(IllegalArgumentException.class);
-    throwable.expectMessage("startOffset (18) should be < endOffset (15) for file [moduleKey=foo, relative=src/Foo.java, basedir=null].");
-
-    new DefaultHighlighting(mock(SensorStorage.class))
-      .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(20))
-      .highlight(0, 10, KEYWORD)
-      .highlight(18, 15, KEYWORD)
-      .save();
-  }
-
 }
index 0be267797b713103d7c5d6e9ee6f4ee02ee218ab..c1784a24dfcc60e9ed94404ae2963c35dce336e8 100644 (file)
@@ -26,6 +26,7 @@ import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputDir;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
 import org.sonar.api.batch.sensor.highlighting.TypeOfText;
@@ -34,6 +35,7 @@ import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.rule.RuleKey;
 
 import java.io.File;
+import java.io.StringReader;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -142,15 +144,15 @@ public class SensorContextTesterTest {
 
   @Test
   public void testHighlighting() {
-    assertThat(tester.highlightingTypeFor("foo:src/Foo.java", 3)).isEmpty();
+    assertThat(tester.highlightingTypeAt("foo:src/Foo.java", 1, 3)).isEmpty();
     tester.newHighlighting()
-      .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(100))
+      .onFile(new DefaultInputFile("foo", "src/Foo.java").initMetadata(new FileMetadata().readMetadata(new StringReader("annot dsf fds foo bar"))))
       .highlight(0, 4, TypeOfText.ANNOTATION)
       .highlight(8, 10, TypeOfText.CONSTANT)
       .highlight(9, 10, TypeOfText.COMMENT)
       .save();
-    assertThat(tester.highlightingTypeFor("foo:src/Foo.java", 3)).containsExactly(TypeOfText.ANNOTATION);
-    assertThat(tester.highlightingTypeFor("foo:src/Foo.java", 9)).containsExactly(TypeOfText.CONSTANT, TypeOfText.COMMENT);
+    assertThat(tester.highlightingTypeAt("foo:src/Foo.java", 1, 3)).containsExactly(TypeOfText.ANNOTATION);
+    assertThat(tester.highlightingTypeAt("foo:src/Foo.java", 1, 9)).containsExactly(TypeOfText.CONSTANT, TypeOfText.COMMENT);
   }
 
   @Test
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/batch/fs/internal/glyphicons-halflings-regular.woff b/sonar-plugin-api/src/test/resources/org/sonar/api/batch/fs/internal/glyphicons-halflings-regular.woff
new file mode 100644 (file)
index 0000000..2cc3e48
Binary files /dev/null and b/sonar-plugin-api/src/test/resources/org/sonar/api/batch/fs/internal/glyphicons-halflings-regular.woff differ