]> source.dussan.org Git - sonarqube.git/commitdiff
Minor refactoring
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Wed, 25 Apr 2018 13:42:40 +0000 (15:42 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 9 May 2018 18:20:46 +0000 (20:20 +0200)
59 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/source/FileSourceDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/source/FileSourceDto.java
server/sonar-db-dao/src/test/java/org/sonar/db/source/FileSourceDaoTest.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/batch/BatchReportReader.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/batch/BatchReportReaderImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssigner.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculator.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoDbLoader.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepository.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ComputeFileSourceData.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReader.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReader.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReader.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LastCommitVisitor.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LineReader.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverter.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReader.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiff.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffFinder.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReader.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReader.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReader.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReader.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/LineReader.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/RangeOffsetConverter.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReader.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReader.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/package-info.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewCoverageMeasuresStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewSizeMeasuresStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistFileSourcesStep.java
server/sonar-server/src/main/java/org/sonar/server/source/SourceService.java
server/sonar-server/src/main/java/org/sonar/server/source/ws/HashAction.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoDbLoaderTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImplTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ComputeFileSourceDataTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReaderTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReaderTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReaderTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverterTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReaderTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffFinderTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffImplTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryImplTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReaderTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReaderTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReaderTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReaderTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReaderTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReaderTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistTestsStepTest.java
sonar-core/src/main/java/org/sonar/core/hash/SourceLinesHashesComputer.java
sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportReader.java

index b660050cde9e89af79cc1e09d0f114ab887df214..7921adb039f29bef19c4617c67422e15771773e2 100644 (file)
@@ -27,7 +27,7 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Collections;
 import java.util.List;
-import java.util.function.Function;
+import java.util.function.Consumer;
 import javax.annotation.CheckForNull;
 import org.apache.commons.dbutils.DbUtils;
 import org.apache.commons.io.IOUtils;
@@ -45,7 +45,7 @@ public class FileSourceDao implements Dao {
   }
 
   @CheckForNull
-  public FileSourceDto selectTest(DbSession dbSession, String fileUuid) {
+  public FileSourceDto selectTestByFileUuid(DbSession dbSession, String fileUuid) {
     return mapper(dbSession).select(fileUuid, Type.TEST);
   }
 
@@ -74,7 +74,7 @@ public class FileSourceDao implements Dao {
     }
   }
 
-  public <T> void readLineHashesStream(DbSession dbSession, String fileUuid, Function<Reader, T> function) {
+  public void readLineHashesStream(DbSession dbSession, String fileUuid, Consumer<Reader> consumer) {
     Connection connection = dbSession.getConnection();
     PreparedStatement pstmt = null;
     ResultSet rs = null;
@@ -87,7 +87,7 @@ public class FileSourceDao implements Dao {
       if (rs.next()) {
         reader = rs.getCharacterStream(1);
         if (reader != null) {
-          function.apply(reader);
+          consumer.accept(reader);
         }
       }
     } catch (SQLException e) {
index 3982bb6c6463d845bfe49623adb59eafa257eb6b..1ac9fbe4043a4422849418e4f0fd6fdb4f1b7505 100644 (file)
@@ -206,7 +206,7 @@ public class FileSourceDto {
   }
 
   /**
-   * Compressed value of serialized protobuf message {@link org.sonar.db.protobuf.DbFileSources.Data}
+   * Decompressed value of serialized protobuf message {@link org.sonar.db.protobuf.DbFileSources.Data}
    */
   public DbFileSources.Data getSourceData() {
     return decodeSourceData(binaryData);
index 718c4ee70967b49961a2cf8247882dae000f01da..13df64a6fec8c6e9ed557e5a588ba5e2efaa597a 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.db.source;
 
 import java.io.IOException;
 import java.io.Reader;
-import java.util.function.Function;
+import java.util.function.Consumer;
 import javax.annotation.Nullable;
 import org.apache.commons.io.IOUtils;
 import org.junit.Rule;
@@ -63,7 +63,7 @@ public class FileSourceDaoTest {
   public void select_line_hashes() {
     dbTester.prepareDbUnit(getClass(), "shared.xml");
 
-    ReaderToStringFunction fn = new ReaderToStringFunction();
+    ReaderToStringConsumer fn = new ReaderToStringConsumer();
     underTest.readLineHashesStream(dbTester.getSession(), "FILE1_UUID", fn);
 
     assertThat(fn.result).isEqualTo("ABC\\nDEF\\nGHI");
@@ -73,7 +73,7 @@ public class FileSourceDaoTest {
   public void no_line_hashes_on_unknown_file() {
     dbTester.prepareDbUnit(getClass(), "shared.xml");
 
-    ReaderToStringFunction fn = new ReaderToStringFunction();
+    ReaderToStringConsumer fn = new ReaderToStringConsumer();
     underTest.readLineHashesStream(dbTester.getSession(), "unknown", fn);
 
     assertThat(fn.result).isNull();
@@ -83,7 +83,7 @@ public class FileSourceDaoTest {
   public void no_line_hashes_when_only_test_data() {
     dbTester.prepareDbUnit(getClass(), "no_line_hashes_when_only_test_data.xml");
 
-    ReaderToStringFunction fn = new ReaderToStringFunction();
+    ReaderToStringConsumer fn = new ReaderToStringConsumer();
     underTest.readLineHashesStream(dbTester.getSession(), "FILE1_UUID", fn);
 
     assertThat(fn.result).isNull();
@@ -115,15 +115,15 @@ public class FileSourceDaoTest {
     dbTester.prepareDbUnit(getClass(), "shared.xml");
 
     underTest.insert(session, new FileSourceDto()
-        .setProjectUuid("PRJ_UUID")
-        .setFileUuid("FILE2_UUID")
-        .setBinaryData("FILE2_BINARY_DATA".getBytes())
-        .setDataHash("FILE2_DATA_HASH")
-        .setSrcHash("FILE2_HASH")
-        .setDataType(Type.SOURCE)
-        .setCreatedAt(1500000000000L)
-        .setUpdatedAt(1500000000001L)
-        .setRevision("123456789"));
+      .setProjectUuid("PRJ_UUID")
+      .setFileUuid("FILE2_UUID")
+      .setBinaryData("FILE2_BINARY_DATA".getBytes())
+      .setDataHash("FILE2_DATA_HASH")
+      .setSrcHash("FILE2_HASH")
+      .setDataType(Type.SOURCE)
+      .setCreatedAt(1500000000000L)
+      .setUpdatedAt(1500000000001L)
+      .setRevision("123456789"));
     session.commit();
 
     assertThat(underTest.selectLineHashes(dbTester.getSession(), "FILE2_UUID")).isEmpty();
@@ -134,25 +134,23 @@ public class FileSourceDaoTest {
     dbTester.prepareDbUnit(getClass(), "shared.xml");
 
     underTest.insert(session, new FileSourceDto()
-        .setProjectUuid("PRJ_UUID")
-        .setFileUuid("FILE2_UUID")
-        .setBinaryData("FILE2_BINARY_DATA".getBytes())
-        .setDataHash("FILE2_DATA_HASH")
-        .setSrcHash("FILE2_HASH")
-        .setDataType(Type.SOURCE)
-        .setCreatedAt(1500000000000L)
-        .setUpdatedAt(1500000000001L)
-        .setRevision("123456789"));
+      .setProjectUuid("PRJ_UUID")
+      .setFileUuid("FILE2_UUID")
+      .setBinaryData("FILE2_BINARY_DATA".getBytes())
+      .setDataHash("FILE2_DATA_HASH")
+      .setSrcHash("FILE2_HASH")
+      .setDataType(Type.SOURCE)
+      .setCreatedAt(1500000000000L)
+      .setUpdatedAt(1500000000001L)
+      .setRevision("123456789"));
     session.commit();
 
     boolean[] flag = {false};
-    underTest.readLineHashesStream(dbTester.getSession(), "FILE2_UUID", new Function<Reader, Void>() {
-      @Nullable
+    underTest.readLineHashesStream(dbTester.getSession(), "FILE2_UUID", new Consumer<Reader>() {
       @Override
-      public Void apply(@Nullable Reader input) {
+      public void accept(@Nullable Reader input) {
         fail("function must never been called since there is no data to read");
         flag[0] = true;
-        return null;
       }
     });
     assertThat(flag[0]).isFalse();
@@ -179,15 +177,14 @@ public class FileSourceDaoTest {
       "project_uuid", "file_uuid", "data_hash", "line_hashes", "src_hash", "created_at", "updated_at", "data_type", "revision");
   }
 
-  private static class ReaderToStringFunction implements Function<Reader, String> {
+  private static class ReaderToStringConsumer implements Consumer<Reader> {
 
     String result = null;
 
     @Override
-    public String apply(Reader input) {
+    public void accept(Reader input) {
       try {
         result = IOUtils.toString(input);
-        return IOUtils.toString(input);
       } catch (IOException e) {
         throw new RuntimeException(e);
       }
index 9557e969e2c10e23af77d5826303cdb0940fa4ac..80adfdee00a3acbbd914bc5e287b960b63cd9822 100644 (file)
@@ -53,7 +53,7 @@ public interface BatchReportReader {
   CloseableIterator<ScannerReport.LineCoverage> readComponentCoverage(int fileRef);
 
   /**
-   * Reads file source line by line. Return an absent optional if the file doest not exist
+   * Reads a file's source code, line by line. Returns an absent optional if the file does not exist
    */
   Optional<CloseableIterator<String>> readFileSource(int fileRef);
 
@@ -62,4 +62,6 @@ public interface BatchReportReader {
   CloseableIterator<ScannerReport.CoverageDetail> readCoverageDetails(int testFileRef);
 
   CloseableIterator<ScannerReport.ContextProperty> readContextProperties();
+  
+  CloseableIterator<ScannerReport.LineSgnificantCode> readComponentSignificantCode(int fileRef);
 }
index b7dc446fe28734c7138e558c62c84a6ca0adb9fc..e9f2c81ace3ec8c3dafc92cfe6ac6748bcb85df1 100644 (file)
@@ -36,6 +36,7 @@ import org.apache.commons.io.LineIterator;
 import org.sonar.core.util.CloseableIterator;
 import org.sonar.core.util.LineReaderIterator;
 import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.LineSgnificantCode;
 
 public class BatchReportReaderImpl implements BatchReportReader {
 
@@ -254,4 +255,10 @@ public class BatchReportReaderImpl implements BatchReportReader {
       fileInputStream.close();
     }
   }
+
+  @Override
+  public CloseableIterator<LineSgnificantCode> readComponentSignificantCode(int fileRef) {
+    ensureInitialized();
+    return delegate.readComponentSignificantCode(fileRef);
+  }
 }
index d366cb1152640eb9368ac99164f0a0d1db420550..ec270607c4340280a3b6c67a1e6da95705e18ee3 100644 (file)
@@ -19,9 +19,9 @@
  */
 package org.sonar.server.computation.task.projectanalysis.issue;
 
-import com.google.common.base.Optional;
 import com.google.common.base.Strings;
 import java.util.Date;
+import java.util.Optional;
 import javax.annotation.CheckForNull;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.utils.log.Logger;
index 70a8a66633a41ee8246b3b14418061fa7c6a8e25..a43250189d3849f73b4394482fa69895cd29b105 100644 (file)
@@ -139,7 +139,7 @@ public class IssueCreationDateCalculator extends IssueVisitor {
   }
 
   private Optional<ScmInfo> getScmInfo(Component component) {
-    return toJavaUtilOptional(scmInfoRepository.getScmInfo(component));
+    return scmInfoRepository.getScmInfo(component);
   }
 
   private static Optional<Changeset> getChangeset(Component component, ScmInfo scmInfo, DefaultIssue issue) {
index 86df58c3fe95c531fece8dd3881d836dacea8bef..cc69b2a5ae140d98b0b52e56faa8fb40e27a91ca 100644 (file)
@@ -165,7 +165,7 @@ public class NewMaintainabilityMeasuresVisitor extends PathAwareVisitorAdapter<N
       return;
     }
 
-    Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(file);
+    java.util.Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(file);
     if (!scmInfoOptional.isPresent()) {
       LOG.trace(String.format("No changeset for file %s. Dev cost will be zero.", file.getKey()));
       return;
index 7ee76a4f646abebd1de2da38b49b5674e8839ab1..4225dff3b5954fa558b960f308432e0007c8de54 100644 (file)
@@ -49,7 +49,7 @@ public class ScmInfoDbLoader {
       return Optional.empty();
     }
 
-    LOGGER.trace("Reading SCM info from db for file '{}'", uuid.get());
+    LOGGER.trace("Reading SCM info from DB for file '{}'", uuid.get());
     try (DbSession dbSession = dbClient.openSession(false)) {
       FileSourceDto dto = dbClient.fileSourceDao().selectSourceByFileUuid(dbSession, uuid.get());
       if (dto == null) {
index 8daffdaa74922a622fb6604b2d8174fcdb708d8e..2a80de6d63a0acc68fc528db60acdb55f541c5a4 100644 (file)
@@ -19,7 +19,7 @@
  */
 package org.sonar.server.computation.task.projectanalysis.scm;
 
-import com.google.common.base.Optional;
+import java.util.Optional;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 
 /**
index 24e714a5683705a798070078b905513fb4dd0e7d..c4d89610db9a79e51e7919bc7ce3db992354c5c3 100644 (file)
@@ -58,18 +58,14 @@ public class ScmInfoRepositoryImpl implements ScmInfoRepository {
   }
 
   @Override
-  public com.google.common.base.Optional<ScmInfo> getScmInfo(Component component) {
+  public Optional<ScmInfo> getScmInfo(Component component) {
     requireNonNull(component, "Component cannot be null");
 
     if (component.getType() != Component.Type.FILE) {
-      return com.google.common.base.Optional.absent();
+      return Optional.empty();
     }
 
-    return toGuavaOptional(scmInfoCache.computeIfAbsent(component, this::getScmInfoForComponent));
-  }
-
-  private static com.google.common.base.Optional<ScmInfo> toGuavaOptional(Optional<ScmInfo> scmInfo) {
-    return com.google.common.base.Optional.fromNullable(scmInfo.orElse(null));
+    return scmInfoCache.computeIfAbsent(component, this::getScmInfoForComponent);
   }
 
   private Optional<ScmInfo> getScmInfoForComponent(Component component) {
@@ -77,7 +73,7 @@ public class ScmInfoRepositoryImpl implements ScmInfoRepository {
 
     if (changesets == null) {
       LOGGER.trace("No SCM info for file '{}'", component.getKey());
-      // SCM not available. It might have been available before - don't keep author and revision.
+      // SCM not available. It might have been available before - copy information for unchanged lines but don't keep author and revision.
       return generateAndMergeDb(component, false);
     }
 
@@ -111,6 +107,12 @@ public class ScmInfoRepositoryImpl implements ScmInfoRepository {
     return Changeset.newChangesetBuilder().setDate(changeset.getDate()).build();
   }
 
+  /**
+   * Get SCM information in the DB, if it exists, and use it for lines that didn't change. It optionally removes author and revision 
+   * information (only keeping change dates).
+   * If the information is not present in the DB or some lines don't match existing lines in the DB, 
+   * we generate change dates based on the analysis date. 
+   */
   private Optional<ScmInfo> generateAndMergeDb(Component file, boolean keepAuthorAndRevision) {
     Optional<DbScmInfo> dbInfoOpt = scmInfoDbLoader.getScmInfo(file);
     if (!dbInfoOpt.isPresent()) {
@@ -125,7 +127,7 @@ public class ScmInfoRepositoryImpl implements ScmInfoRepository {
     }
 
     // generate date for new/changed lines
-    int[] matchingLines = sourceLinesDiff.getMatchingLines(file);
+    int[] matchingLines = sourceLinesDiff.computeMatchingLines(file);
 
     return Optional.of(GeneratedScmInfo.create(analysisMetadata.getAnalysisDate(), matchingLines, scmInfo));
   }
index 45f40f648e4db97ab1642cb566525bcc9d109aeb..dd06717d66d78c27c196947884db347d68a1a50a 100644 (file)
@@ -25,6 +25,7 @@ import java.util.List;
 import org.sonar.core.hash.SourceHashComputer;
 import org.sonar.core.hash.SourceLinesHashesComputer;
 import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.LineReader;
 
 public class ComputeFileSourceData {
 
@@ -50,12 +51,13 @@ public class ComputeFileSourceData {
     return data;
   }
 
-  private void read(Data data, String source, boolean hasNextLine) {
-    data.linesHashesComputer.addLine(source);
-    data.sourceHashComputer.addLine(source, hasNextLine);
+  private void read(Data data, String lineSource, boolean hasNextLine) {
+    data.linesHashesComputer.addLine(lineSource);
+    data.sourceHashComputer.addLine(lineSource, hasNextLine);
 
-    DbFileSources.Line.Builder lineBuilder = data.fileSourceBuilder.addLinesBuilder()
-      .setSource(source)
+    DbFileSources.Line.Builder lineBuilder = data.fileSourceBuilder
+      .addLinesBuilder()
+      .setSource(lineSource)
       .setLine(currentLine);
     for (LineReader lineReader : lineReaders) {
       lineReader.read(lineBuilder);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReader.java
deleted file mode 100644 (file)
index 8bfc664..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import java.util.Iterator;
-import javax.annotation.CheckForNull;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.scanner.protocol.output.ScannerReport;
-import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.HasCoveredConditionsCase;
-import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.HasHitsCase;
-
-public class CoverageLineReader implements LineReader {
-
-  private final Iterator<ScannerReport.LineCoverage> coverageIterator;
-  private ScannerReport.LineCoverage coverage;
-
-  public CoverageLineReader(Iterator<ScannerReport.LineCoverage> coverageIterator) {
-    this.coverageIterator = coverageIterator;
-  }
-
-  @Override
-  public void read(DbFileSources.Line.Builder lineBuilder) {
-    ScannerReport.LineCoverage reportCoverage = getNextLineCoverageIfMatchLine(lineBuilder.getLine());
-    if (reportCoverage != null) {
-      processCoverage(lineBuilder, reportCoverage);
-      coverage = null;
-    }
-  }
-
-  private static void processCoverage(DbFileSources.Line.Builder lineBuilder, ScannerReport.LineCoverage reportCoverage) {
-    if (reportCoverage.getHasHitsCase() == HasHitsCase.HITS) {
-      lineBuilder.setLineHits(reportCoverage.getHits() ? 1 : 0);
-    }
-    if (reportCoverage.getHasCoveredConditionsCase() == HasCoveredConditionsCase.COVERED_CONDITIONS) {
-      lineBuilder.setConditions(reportCoverage.getConditions());
-      lineBuilder.setCoveredConditions(reportCoverage.getCoveredConditions());
-    }
-  }
-
-  @CheckForNull
-  private ScannerReport.LineCoverage getNextLineCoverageIfMatchLine(int line) {
-    // Get next element (if exists)
-    if (coverage == null && coverageIterator.hasNext()) {
-      coverage = coverageIterator.next();
-    }
-    // Return current element if lines match
-    if (coverage != null && coverage.getLine() == line) {
-      return coverage;
-    }
-    return null;
-  }
-
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReader.java
deleted file mode 100644 (file)
index afd7e87..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Ordering;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.server.computation.task.projectanalysis.duplication.Duplication;
-import org.sonar.server.computation.task.projectanalysis.duplication.InnerDuplicate;
-import org.sonar.server.computation.task.projectanalysis.duplication.TextBlock;
-
-import static com.google.common.collect.FluentIterable.from;
-import static com.google.common.collect.Iterables.size;
-
-public class DuplicationLineReader implements LineReader {
-
-  private final Map<TextBlock, Integer> duplicatedTextBlockIndexByTextBlock;
-
-  public DuplicationLineReader(Iterable<Duplication> duplications) {
-    this.duplicatedTextBlockIndexByTextBlock = createIndexOfDuplicatedTextBlocks(duplications);
-  }
-
-  @Override
-  public void read(DbFileSources.Line.Builder lineBuilder) {
-    Predicate<Map.Entry<TextBlock, Integer>> containsLine = new TextBlockContainsLine(lineBuilder.getLine());
-    for (Integer textBlockIndex : from(duplicatedTextBlockIndexByTextBlock.entrySet())
-      .filter(containsLine)
-      .transform(MapEntryToBlockId.INSTANCE)
-      // list is sorted to cope with the non-guaranteed order of Map entries which would trigger false detection of changes
-      // in {@link DbFileSources.Line#getDuplicationList()}
-      .toSortedList(Ordering.natural())) {
-      lineBuilder.addDuplication(textBlockIndex);
-    }
-  }
-
-  /**
-   *
-   * <p>
-   * This method uses the natural order of TextBlocks to ensure that given the same set of TextBlocks, they get the same
-   * index. It avoids false detections of changes in {@link DbFileSources.Line#getDuplicationList()}.
-   * </p>
-   */
-  private static Map<TextBlock, Integer> createIndexOfDuplicatedTextBlocks(Iterable<Duplication> duplications) {
-    List<TextBlock> duplicatedTextBlocks = extractAllDuplicatedTextBlocks(duplications);
-    Collections.sort(duplicatedTextBlocks);
-    return from(duplicatedTextBlocks)
-      .toMap(new TextBlockIndexGenerator());
-  }
-
-  /**
-   * Duplicated blocks in the current file are either {@link Duplication#getOriginal()} or {@link Duplication#getDuplicates()}
-   * when the {@link org.sonar.server.computation.task.projectanalysis.duplication.Duplicate} is a {@link InnerDuplicate}.
-   * <p>
-   * The returned list is mutable on purpose because it will be sorted.
-   * </p>
-   *
-   * @see {@link #createIndexOfDuplicatedTextBlocks(Iterable)}
-   */
-  private static List<TextBlock> extractAllDuplicatedTextBlocks(Iterable<Duplication> duplications) {
-    List<TextBlock> duplicatedBlock = new ArrayList<>(size(duplications));
-    for (Duplication duplication : duplications) {
-      duplicatedBlock.add(duplication.getOriginal());
-      for (InnerDuplicate duplicate : from(duplication.getDuplicates()).filter(InnerDuplicate.class)) {
-        duplicatedBlock.add(duplicate.getTextBlock());
-      }
-    }
-    return duplicatedBlock;
-  }
-
-  private static class TextBlockContainsLine implements Predicate<Map.Entry<TextBlock, Integer>> {
-    private final int line;
-
-    public TextBlockContainsLine(int line) {
-      this.line = line;
-    }
-
-    @Override
-    public boolean apply(@Nonnull Map.Entry<TextBlock, Integer> input) {
-      return isLineInBlock(input.getKey(), line);
-    }
-
-    private static boolean isLineInBlock(TextBlock range, int line) {
-      return line >= range.getStart() && line <= range.getEnd();
-    }
-  }
-
-  private enum MapEntryToBlockId implements Function<Map.Entry<TextBlock, Integer>, Integer> {
-    INSTANCE;
-
-    @Override
-    @Nonnull
-    public Integer apply(@Nonnull Map.Entry<TextBlock, Integer> input) {
-      return input.getValue();
-    }
-  }
-
-  private static class TextBlockIndexGenerator implements Function<TextBlock, Integer> {
-    int i = 1;
-
-    @Nullable
-    @Override
-    public Integer apply(TextBlock input) {
-      return i++;
-    }
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReader.java
deleted file mode 100644 (file)
index a758ebb..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import com.google.common.collect.ImmutableMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import javax.annotation.CheckForNull;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.scanner.protocol.output.ScannerReport;
-import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
-import org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.RangeOffsetConverterException;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static java.lang.String.format;
-import static org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.OFFSET_SEPARATOR;
-import static org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.SYMBOLS_SEPARATOR;
-
-public class HighlightingLineReader implements LineReader {
-
-  private static final Logger LOG = Loggers.get(HighlightingLineReader.class);
-
-  private boolean isHighlightingValid = true;
-
-  private static final Map<HighlightingType, String> cssClassByType = ImmutableMap.<HighlightingType, String>builder()
-    .put(HighlightingType.ANNOTATION, "a")
-    .put(HighlightingType.CONSTANT, "c")
-    .put(HighlightingType.COMMENT, "cd")
-    .put(HighlightingType.CPP_DOC, "cppd")
-    .put(HighlightingType.STRUCTURED_COMMENT, "j")
-    .put(HighlightingType.KEYWORD, "k")
-    .put(HighlightingType.KEYWORD_LIGHT, "h")
-    .put(HighlightingType.HIGHLIGHTING_STRING, "s")
-    .put(HighlightingType.PREPROCESS_DIRECTIVE, "p")
-    .build();
-
-  private final Component file;
-  private final Iterator<ScannerReport.SyntaxHighlightingRule> lineHighlightingIterator;
-  private final RangeOffsetConverter rangeOffsetConverter;
-  private final List<ScannerReport.SyntaxHighlightingRule> highlightingList;
-
-  private ScannerReport.SyntaxHighlightingRule currentItem;
-
-  public HighlightingLineReader(Component file, Iterator<ScannerReport.SyntaxHighlightingRule> lineHighlightingIterator, RangeOffsetConverter rangeOffsetConverter) {
-    this.file = file;
-    this.lineHighlightingIterator = lineHighlightingIterator;
-    this.rangeOffsetConverter = rangeOffsetConverter;
-    this.highlightingList = newArrayList();
-  }
-
-  @Override
-  public void read(DbFileSources.Line.Builder lineBuilder) {
-    if (!isHighlightingValid) {
-      return;
-    }
-    try {
-      processHighlightings(lineBuilder);
-    } catch (RangeOffsetConverterException e) {
-      isHighlightingValid = false;
-      LOG.warn(format("Inconsistency detected in Highlighting data. Highlighting will be ignored for file '%s'", file.getKey()), e);
-    }
-  }
-
-  private void processHighlightings(DbFileSources.Line.Builder lineBuilder) {
-    int line = lineBuilder.getLine();
-    StringBuilder highlighting = new StringBuilder();
-
-    incrementHighlightingListMatchingLine(line);
-    for (Iterator<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingIterator = highlightingList.iterator(); syntaxHighlightingIterator.hasNext();) {
-      processHighlighting(syntaxHighlightingIterator, highlighting, lineBuilder);
-    }
-    if (highlighting.length() > 0) {
-      lineBuilder.setHighlighting(highlighting.toString());
-    }
-  }
-
-  private void processHighlighting(Iterator<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingIterator, StringBuilder highlighting,
-    DbFileSources.Line.Builder lineBuilder) {
-    ScannerReport.SyntaxHighlightingRule syntaxHighlighting = syntaxHighlightingIterator.next();
-    int line = lineBuilder.getLine();
-    ScannerReport.TextRange range = syntaxHighlighting.getRange();
-    if (range.getStartLine() <= line) {
-      String offsets = rangeOffsetConverter.offsetToString(syntaxHighlighting.getRange(), line, lineBuilder.getSource().length());
-      if (offsets.isEmpty()) {
-        if (range.getEndLine() == line) {
-          syntaxHighlightingIterator.remove();
-        }
-      } else {
-        if (highlighting.length() > 0) {
-          highlighting.append(SYMBOLS_SEPARATOR);
-        }
-        highlighting.append(offsets)
-          .append(OFFSET_SEPARATOR)
-          .append(getCssClass(syntaxHighlighting.getType()));
-        if (range.getEndLine() == line) {
-          syntaxHighlightingIterator.remove();
-        }
-      }
-    }
-  }
-
-  private static String getCssClass(HighlightingType type) {
-    String cssClass = cssClassByType.get(type);
-    if (cssClass != null) {
-      return cssClass;
-    } else {
-      throw new IllegalArgumentException(format("Unknown type %s ", type.toString()));
-    }
-  }
-
-  private void incrementHighlightingListMatchingLine(int line) {
-    ScannerReport.SyntaxHighlightingRule syntaxHighlighting = getNextHighlightingMatchingLine(line);
-    while (syntaxHighlighting != null) {
-      highlightingList.add(syntaxHighlighting);
-      this.currentItem = null;
-      syntaxHighlighting = getNextHighlightingMatchingLine(line);
-    }
-  }
-
-  @CheckForNull
-  private ScannerReport.SyntaxHighlightingRule getNextHighlightingMatchingLine(int line) {
-    // Get next element (if exists)
-    if (currentItem == null && lineHighlightingIterator.hasNext()) {
-      currentItem = lineHighlightingIterator.next();
-    }
-    // Return current element if lines match
-    if (currentItem != null && currentItem.getRange().getStartLine() == line) {
-      return currentItem;
-    }
-    return null;
-  }
-
-}
index 9260a8b22ca5fb70041eb62d4632f6ee83e8e788..584598b38341e8fbb0b216b20258dac1d523541e 100644 (file)
@@ -78,7 +78,7 @@ public class LastCommitVisitor extends PathAwareVisitorAdapter<LastCommitVisitor
     // since previous analysis (optimization to decrease execution of blame commands). In this case
     // the date is loaded from database, as it did not change from previous analysis.
 
-    Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(file);
+    java.util.Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(file);
     if (scmInfoOptional.isPresent()) {
       ScmInfo scmInfo = scmInfoOptional.get();
       path.current().addDate(scmInfo.getLatestChangeset().getDate());
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LineReader.java
deleted file mode 100644 (file)
index b688dee..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import org.sonar.db.protobuf.DbFileSources;
-
-public interface LineReader {
-
-  void read(DbFileSources.Line.Builder lineBuilder);
-
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverter.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverter.java
deleted file mode 100644 (file)
index 86083eb..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import org.sonar.scanner.protocol.output.ScannerReport;
-
-import static java.lang.String.format;
-
-public class RangeOffsetConverter {
-
-  static final String OFFSET_SEPARATOR = ",";
-  static final String SYMBOLS_SEPARATOR = ";";
-
-  public String offsetToString(ScannerReport.TextRange range, int lineIndex, int lineLength) {
-    validateOffsetOrder(range, lineIndex);
-    validateStartOffsetNotGreaterThanLineLength(range, lineLength, lineIndex);
-    validateEndOffsetNotGreaterThanLineLength(range, lineLength, lineIndex);
-
-    int startOffset = range.getStartLine() == lineIndex ? range.getStartOffset() : 0;
-    int endOffset = range.getEndLine() == lineIndex ? range.getEndOffset() : lineLength;
-
-    StringBuilder element = new StringBuilder();
-    if (startOffset < endOffset) {
-      element.append(startOffset).append(OFFSET_SEPARATOR);
-      element.append(endOffset);
-    }
-
-    return element.toString();
-  }
-
-  private static void validateOffsetOrder(ScannerReport.TextRange range, int line) {
-    checkExpression(range.getStartLine() != range.getEndLine() || range.getStartOffset() <= range.getEndOffset(),
-      "End offset %s cannot be defined before start offset %s on line %s", range.getEndOffset(), range.getStartOffset(), line);
-  }
-
-  private static void validateStartOffsetNotGreaterThanLineLength(ScannerReport.TextRange range, int lineLength, int line) {
-    checkExpression(range.getStartLine() != line || range.getStartOffset() <= lineLength,
-      "Start offset %s is defined outside the length (%s) of the line %s", range.getStartOffset(), lineLength, line);
-  }
-
-  private static void validateEndOffsetNotGreaterThanLineLength(ScannerReport.TextRange range, int lineLength, int line) {
-    checkExpression(range.getEndLine() != line || range.getEndOffset() <= lineLength,
-      "End offset %s is defined outside the length (%s) of the line %s", range.getEndOffset(), lineLength, line);
-  }
-
-  private static void checkExpression(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
-    if (!expression) {
-      throw new RangeOffsetConverterException(format(errorMessageTemplate, errorMessageArgs));
-    }
-  }
-
-  public static class RangeOffsetConverterException extends RuntimeException {
-    public RangeOffsetConverterException(String message) {
-      super(message);
-    }
-  }
-
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReader.java
deleted file mode 100644 (file)
index b09edff..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import javax.annotation.CheckForNull;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.server.computation.task.projectanalysis.scm.Changeset;
-import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo;
-
-public class ScmLineReader implements LineReader {
-
-  private final ScmInfo scmReport;
-  @CheckForNull
-  private Changeset latestChange;
-  @CheckForNull
-  private Changeset latestChangeWithRevision;
-
-  public ScmLineReader(ScmInfo scmReport) {
-    this.scmReport = scmReport;
-  }
-
-  @Override
-  public void read(DbFileSources.Line.Builder lineBuilder) {
-    if (scmReport.hasChangesetForLine(lineBuilder.getLine())) {
-      Changeset changeset = scmReport.getChangesetForLine(lineBuilder.getLine());
-      String author = changeset.getAuthor();
-      if (author != null) {
-        lineBuilder.setScmAuthor(author);
-      }
-      String revision = changeset.getRevision();
-      if (revision != null) {
-        lineBuilder.setScmRevision(revision);
-      }
-      lineBuilder.setScmDate(changeset.getDate());
-      updateLatestChange(changeset);
-
-      if (revision != null) {
-        updateLatestChangeWithRevision(changeset);
-      }
-    }
-  }
-
-  private void updateLatestChange(Changeset newChangeSet) {
-    if (latestChange == null) {
-      latestChange = newChangeSet;
-    } else {
-      long newChangesetDate = newChangeSet.getDate();
-      long latestChangeDate = latestChange.getDate();
-      if (newChangesetDate > latestChangeDate) {
-        latestChange = newChangeSet;
-      }
-    }
-  }
-
-  private void updateLatestChangeWithRevision(Changeset newChangeSet) {
-    if (latestChangeWithRevision == null) {
-      latestChangeWithRevision = newChangeSet;
-    } else {
-      long newChangesetDate = newChangeSet.getDate();
-      long latestChangeDate = latestChangeWithRevision.getDate();
-      if (newChangesetDate > latestChangeDate) {
-        latestChangeWithRevision = newChangeSet;
-      }
-    }
-  }
-
-  @CheckForNull
-  public Changeset getLatestChangeWithRevision() {
-    return latestChangeWithRevision;
-  }
-
-  @CheckForNull
-  public Changeset getLatestChange() {
-    return latestChange;
-  }
-}
index 61b6763df88a878d062a2d765868f8549a123645..99e330320041961d5cfb653c8b7b76f687fb05fa 100644 (file)
@@ -28,5 +28,5 @@ public interface SourceLinesDiff {
    * @return an array with one entry for each line in the left side. Those entries point either to a line in the right side, or to 0, 
    * in which case it means the line was added.
    */
-  int[] getMatchingLines(Component component);
+  int[] computeMatchingLines(Component component);
 }
index f0bc058c6959237ced6a2422a4b711fc4f3d4af0..e4ac5938b686e804520897199d68d52ea2390016 100644 (file)
@@ -28,15 +28,8 @@ import org.sonar.api.utils.log.Loggers;
 
 public class SourceLinesDiffFinder {
   private static final Logger LOG = Loggers.get(SourceLinesDiffFinder.class);
-  private final List<String> left;
-  private final List<String> right;
 
-  public SourceLinesDiffFinder(List<String> left, List<String> right) {
-    this.left = left;
-    this.right = right;
-  }
-
-  public int[] findMatchingLines() {
+  public int[] findMatchingLines(List<String> left, List<String> right) {
     int[] index = new int[right.size()];
 
     int dbLine = left.size();
index 904072747df28b8d2818f168f3670fc4bcd28d7a..02e08eea5a79f9a14c1259fa4e59c2cfce924d9f 100644 (file)
@@ -19,7 +19,7 @@
  */
 package org.sonar.server.computation.task.projectanalysis.source;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import org.sonar.core.hash.SourceLinesHashesComputer;
 import org.sonar.core.util.CloseableIterator;
@@ -42,17 +42,25 @@ public class SourceLinesDiffImpl implements SourceLinesDiff {
   }
 
   @Override
-  public int[] getMatchingLines(Component component) {
+  public int[] computeMatchingLines(Component component) {
 
-    List<String> database;
+    List<String> database = getDBLines(component);
+    List<String> report = getReportLines(component);
+
+    return new SourceLinesDiffFinder().findMatchingLines(database, report);
+  }
+
+  private List<String> getDBLines(Component component) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      database = fileSourceDao.selectLineHashes(dbSession, component.getUuid());
+      List<String> database = fileSourceDao.selectLineHashes(dbSession, component.getUuid());
       if (database == null) {
-        database = new ArrayList<>();
+        return Collections.emptyList();
       }
+      return database;
     }
+  }
 
-    List<String> report;
+  private List<String> getReportLines(Component component) {
     SourceLinesHashesComputer linesHashesComputer = new SourceLinesHashesComputer();
     try (CloseableIterator<String> lineIterator = sourceLinesRepository.readLines(component)) {
       while (lineIterator.hasNext()) {
@@ -60,10 +68,7 @@ public class SourceLinesDiffImpl implements SourceLinesDiff {
         linesHashesComputer.addLine(line);
       }
     }
-    report = linesHashesComputer.getLineHashes();
-
-    return new SourceLinesDiffFinder(database, report).findMatchingLines();
-
+    return linesHashesComputer.getLineHashes();
   }
 
 }
index dcb5cff5e8c5a983cb19539417c7a4c4a0c22120..3366a28802be473c60f9945b46adeb45a165db36 100644 (file)
@@ -39,7 +39,7 @@ public class SourceLinesRepositoryImpl implements SourceLinesRepository {
 
   @Override
   public CloseableIterator<String> readLines(Component file) {
-    requireNonNull(file, "Component should not be bull");
+    requireNonNull(file, "Component should not be null");
     checkArgument(file.getType() == FILE, "Component '%s' is not a file", file);
 
     Optional<CloseableIterator<String>> linesIteratorOptional = reportReader.readFileSource(file.getReportAttributes().getRef());
@@ -90,7 +90,7 @@ public class SourceLinesRepositoryImpl implements SourceLinesRepository {
 
     @Override
     protected String doNext() {
-      throw new UnsupportedOperationException("No implemented because hasNext and next are override");
+      throw new UnsupportedOperationException("Not implemented because hasNext() and next() are overriden");
     }
 
     @Override
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReader.java
deleted file mode 100644 (file)
index 486c97c..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.SetMultimap;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.scanner.protocol.output.ScannerReport;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
-
-import static java.lang.String.format;
-import static org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.OFFSET_SEPARATOR;
-import static org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.SYMBOLS_SEPARATOR;
-
-public class SymbolsLineReader implements LineReader {
-
-  private static final Logger LOG = Loggers.get(HighlightingLineReader.class);
-
-  private final Component file;
-  private final RangeOffsetConverter rangeOffsetConverter;
-  private final Map<ScannerReport.Symbol, Integer> idsBySymbol;
-  private final SetMultimap<Integer, ScannerReport.Symbol> symbolsPerLine;
-
-  private boolean areSymbolsValid = true;
-
-  public SymbolsLineReader(Component file, Iterator<ScannerReport.Symbol> symbolIterator, RangeOffsetConverter rangeOffsetConverter) {
-    this.file = file;
-    this.rangeOffsetConverter = rangeOffsetConverter;
-    List<ScannerReport.Symbol> symbols = Lists.newArrayList(symbolIterator);
-    // Sort symbols to have deterministic id generation
-    Collections.sort(symbols, SymbolsComparator.INSTANCE);
-
-    this.idsBySymbol = createIdsBySymbolMap(symbols);
-    this.symbolsPerLine = buildSymbolsPerLine(symbols);
-  }
-
-  @Override
-  public void read(DbFileSources.Line.Builder lineBuilder) {
-    if (!areSymbolsValid) {
-      return;
-    }
-    try {
-      processSymbols(lineBuilder);
-    } catch (RangeOffsetConverter.RangeOffsetConverterException e) {
-      areSymbolsValid = false;
-      LOG.warn(format("Inconsistency detected in Symbols data. Symbols will be ignored for file '%s'", file.getKey()), e);
-    }
-  }
-
-  private void processSymbols(DbFileSources.Line.Builder lineBuilder) {
-    int line = lineBuilder.getLine();
-
-    List<ScannerReport.Symbol> lineSymbols = new ArrayList<>(this.symbolsPerLine.get(line));
-    // Sort symbols to have deterministic results and avoid false variation that would lead to an unnecessary update of the source files
-    // data
-    Collections.sort(lineSymbols, SymbolsComparator.INSTANCE);
-
-    StringBuilder symbolString = new StringBuilder();
-    for (ScannerReport.Symbol lineSymbol : lineSymbols) {
-      int symbolId = idsBySymbol.get(lineSymbol);
-
-      appendSymbol(symbolString, lineSymbol.getDeclaration(), line, symbolId, lineBuilder.getSource());
-      for (ScannerReport.TextRange range : lineSymbol.getReferenceList()) {
-        appendSymbol(symbolString, range, line, symbolId, lineBuilder.getSource());
-      }
-    }
-    if (symbolString.length() > 0) {
-      lineBuilder.setSymbols(symbolString.toString());
-    }
-  }
-
-  private void appendSymbol(StringBuilder lineSymbol, ScannerReport.TextRange range, int line, int symbolId, String sourceLine) {
-    if (matchLine(range, line)) {
-      String offsets = rangeOffsetConverter.offsetToString(range, line, sourceLine.length());
-      if (!offsets.isEmpty()) {
-        if (lineSymbol.length() > 0) {
-          lineSymbol.append(SYMBOLS_SEPARATOR);
-        }
-        lineSymbol.append(offsets)
-          .append(OFFSET_SEPARATOR)
-          .append(symbolId);
-      }
-    }
-  }
-
-  private static boolean matchLine(ScannerReport.TextRange range, int line) {
-    return range.getStartLine() <= line && range.getEndLine() >= line;
-  }
-
-  private static Map<ScannerReport.Symbol, Integer> createIdsBySymbolMap(List<ScannerReport.Symbol> symbols) {
-    Map<ScannerReport.Symbol, Integer> map = new IdentityHashMap<>(symbols.size());
-    int symbolId = 1;
-    for (ScannerReport.Symbol symbol : symbols) {
-      map.put(symbol, symbolId);
-      symbolId++;
-    }
-    return map;
-  }
-
-  private static SetMultimap<Integer, ScannerReport.Symbol> buildSymbolsPerLine(List<ScannerReport.Symbol> symbols) {
-    SetMultimap<Integer, ScannerReport.Symbol> res = HashMultimap.create();
-    for (ScannerReport.Symbol symbol : symbols) {
-      putForTextRange(res, symbol, symbol.getDeclaration());
-      for (ScannerReport.TextRange textRange : symbol.getReferenceList()) {
-        putForTextRange(res, symbol, textRange);
-      }
-    }
-    return res;
-  }
-
-  private static void putForTextRange(SetMultimap<Integer, ScannerReport.Symbol> res, ScannerReport.Symbol symbol, ScannerReport.TextRange declaration) {
-    for (int i = declaration.getStartLine(); i <= declaration.getEndLine(); i++) {
-      res.put(i, symbol);
-    }
-  }
-
-  private enum SymbolsComparator implements Comparator<ScannerReport.Symbol> {
-    INSTANCE;
-
-    @Override
-    public int compare(ScannerReport.Symbol o1, ScannerReport.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());
-      }
-    }
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReader.java
new file mode 100644 (file)
index 0000000..7660491
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import java.util.Iterator;
+import javax.annotation.CheckForNull;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.HasCoveredConditionsCase;
+import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.HasHitsCase;
+
+public class CoverageLineReader implements LineReader {
+
+  private final Iterator<ScannerReport.LineCoverage> coverageIterator;
+  private ScannerReport.LineCoverage coverage;
+
+  public CoverageLineReader(Iterator<ScannerReport.LineCoverage> coverageIterator) {
+    this.coverageIterator = coverageIterator;
+  }
+
+  @Override
+  public void read(DbFileSources.Line.Builder lineBuilder) {
+    ScannerReport.LineCoverage reportCoverage = getNextLineCoverageIfMatchLine(lineBuilder.getLine());
+    if (reportCoverage != null) {
+      processCoverage(lineBuilder, reportCoverage);
+      coverage = null;
+    }
+  }
+
+  private static void processCoverage(DbFileSources.Line.Builder lineBuilder, ScannerReport.LineCoverage reportCoverage) {
+    if (reportCoverage.getHasHitsCase() == HasHitsCase.HITS) {
+      lineBuilder.setLineHits(reportCoverage.getHits() ? 1 : 0);
+    }
+    if (reportCoverage.getHasCoveredConditionsCase() == HasCoveredConditionsCase.COVERED_CONDITIONS) {
+      lineBuilder.setConditions(reportCoverage.getConditions());
+      lineBuilder.setCoveredConditions(reportCoverage.getCoveredConditions());
+    }
+  }
+
+  @CheckForNull
+  private ScannerReport.LineCoverage getNextLineCoverageIfMatchLine(int line) {
+    // Get next element (if exists)
+    if (coverage == null && coverageIterator.hasNext()) {
+      coverage = coverageIterator.next();
+    }
+    // Return current element if lines match
+    if (coverage != null && coverage.getLine() == line) {
+      return coverage;
+    }
+    return null;
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReader.java
new file mode 100644 (file)
index 0000000..ffaa942
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Ordering;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.server.computation.task.projectanalysis.duplication.Duplication;
+import org.sonar.server.computation.task.projectanalysis.duplication.InnerDuplicate;
+import org.sonar.server.computation.task.projectanalysis.duplication.TextBlock;
+
+import static com.google.common.collect.FluentIterable.from;
+import static com.google.common.collect.Iterables.size;
+
+public class DuplicationLineReader implements LineReader {
+
+  private final Map<TextBlock, Integer> duplicatedTextBlockIndexByTextBlock;
+
+  public DuplicationLineReader(Iterable<Duplication> duplications) {
+    this.duplicatedTextBlockIndexByTextBlock = createIndexOfDuplicatedTextBlocks(duplications);
+  }
+
+  @Override
+  public void read(DbFileSources.Line.Builder lineBuilder) {
+    Predicate<Map.Entry<TextBlock, Integer>> containsLine = new TextBlockContainsLine(lineBuilder.getLine());
+    for (Integer textBlockIndex : from(duplicatedTextBlockIndexByTextBlock.entrySet())
+      .filter(containsLine)
+      .transform(MapEntryToBlockId.INSTANCE)
+      // list is sorted to cope with the non-guaranteed order of Map entries which would trigger false detection of changes
+      // in {@link DbFileSources.Line#getDuplicationList()}
+      .toSortedList(Ordering.natural())) {
+      lineBuilder.addDuplication(textBlockIndex);
+    }
+  }
+
+  /**
+   *
+   * <p>
+   * This method uses the natural order of TextBlocks to ensure that given the same set of TextBlocks, they get the same
+   * index. It avoids false detections of changes in {@link DbFileSources.Line#getDuplicationList()}.
+   * </p>
+   */
+  private static Map<TextBlock, Integer> createIndexOfDuplicatedTextBlocks(Iterable<Duplication> duplications) {
+    List<TextBlock> duplicatedTextBlocks = extractAllDuplicatedTextBlocks(duplications);
+    Collections.sort(duplicatedTextBlocks);
+    return from(duplicatedTextBlocks)
+      .toMap(new TextBlockIndexGenerator());
+  }
+
+  /**
+   * Duplicated blocks in the current file are either {@link Duplication#getOriginal()} or {@link Duplication#getDuplicates()}
+   * when the {@link org.sonar.server.computation.task.projectanalysis.duplication.Duplicate} is a {@link InnerDuplicate}.
+   * <p>
+   * The returned list is mutable on purpose because it will be sorted.
+   * </p>
+   *
+   * @see {@link #createIndexOfDuplicatedTextBlocks(Iterable)}
+   */
+  private static List<TextBlock> extractAllDuplicatedTextBlocks(Iterable<Duplication> duplications) {
+    List<TextBlock> duplicatedBlock = new ArrayList<>(size(duplications));
+    for (Duplication duplication : duplications) {
+      duplicatedBlock.add(duplication.getOriginal());
+      for (InnerDuplicate duplicate : from(duplication.getDuplicates()).filter(InnerDuplicate.class)) {
+        duplicatedBlock.add(duplicate.getTextBlock());
+      }
+    }
+    return duplicatedBlock;
+  }
+
+  private static class TextBlockContainsLine implements Predicate<Map.Entry<TextBlock, Integer>> {
+    private final int line;
+
+    public TextBlockContainsLine(int line) {
+      this.line = line;
+    }
+
+    @Override
+    public boolean apply(@Nonnull Map.Entry<TextBlock, Integer> input) {
+      return isLineInBlock(input.getKey(), line);
+    }
+
+    private static boolean isLineInBlock(TextBlock range, int line) {
+      return line >= range.getStart() && line <= range.getEnd();
+    }
+  }
+
+  private enum MapEntryToBlockId implements Function<Map.Entry<TextBlock, Integer>, Integer> {
+    INSTANCE;
+
+    @Override
+    @Nonnull
+    public Integer apply(@Nonnull Map.Entry<TextBlock, Integer> input) {
+      return input.getValue();
+    }
+  }
+
+  private static class TextBlockIndexGenerator implements Function<TextBlock, Integer> {
+    int i = 1;
+
+    @Nullable
+    @Override
+    public Integer apply(TextBlock input) {
+      return i++;
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReader.java
new file mode 100644 (file)
index 0000000..69f1306
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.CheckForNull;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.RangeOffsetConverterException;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static java.lang.String.format;
+import static org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.OFFSET_SEPARATOR;
+import static org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.SYMBOLS_SEPARATOR;
+
+public class HighlightingLineReader implements LineReader {
+
+  private static final Logger LOG = Loggers.get(HighlightingLineReader.class);
+
+  private boolean isHighlightingValid = true;
+
+  private static final Map<HighlightingType, String> cssClassByType = ImmutableMap.<HighlightingType, String>builder()
+    .put(HighlightingType.ANNOTATION, "a")
+    .put(HighlightingType.CONSTANT, "c")
+    .put(HighlightingType.COMMENT, "cd")
+    .put(HighlightingType.CPP_DOC, "cppd")
+    .put(HighlightingType.STRUCTURED_COMMENT, "j")
+    .put(HighlightingType.KEYWORD, "k")
+    .put(HighlightingType.KEYWORD_LIGHT, "h")
+    .put(HighlightingType.HIGHLIGHTING_STRING, "s")
+    .put(HighlightingType.PREPROCESS_DIRECTIVE, "p")
+    .build();
+
+  private final Component file;
+  private final Iterator<ScannerReport.SyntaxHighlightingRule> lineHighlightingIterator;
+  private final RangeOffsetConverter rangeOffsetConverter;
+  private final List<ScannerReport.SyntaxHighlightingRule> highlightingList;
+
+  private ScannerReport.SyntaxHighlightingRule currentItem;
+
+  public HighlightingLineReader(Component file, Iterator<ScannerReport.SyntaxHighlightingRule> lineHighlightingIterator, RangeOffsetConverter rangeOffsetConverter) {
+    this.file = file;
+    this.lineHighlightingIterator = lineHighlightingIterator;
+    this.rangeOffsetConverter = rangeOffsetConverter;
+    this.highlightingList = newArrayList();
+  }
+
+  @Override
+  public void read(DbFileSources.Line.Builder lineBuilder) {
+    if (!isHighlightingValid) {
+      return;
+    }
+    try {
+      processHighlightings(lineBuilder);
+    } catch (RangeOffsetConverterException e) {
+      isHighlightingValid = false;
+      LOG.warn(format("Inconsistency detected in Highlighting data. Highlighting will be ignored for file '%s'", file.getKey()), e);
+    }
+  }
+
+  private void processHighlightings(DbFileSources.Line.Builder lineBuilder) {
+    int line = lineBuilder.getLine();
+    StringBuilder highlighting = new StringBuilder();
+
+    incrementHighlightingListMatchingLine(line);
+    for (Iterator<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingIterator = highlightingList.iterator(); syntaxHighlightingIterator.hasNext();) {
+      processHighlighting(syntaxHighlightingIterator, highlighting, lineBuilder);
+    }
+    if (highlighting.length() > 0) {
+      lineBuilder.setHighlighting(highlighting.toString());
+    }
+  }
+
+  private void processHighlighting(Iterator<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingIterator, StringBuilder highlighting,
+    DbFileSources.Line.Builder lineBuilder) {
+    ScannerReport.SyntaxHighlightingRule syntaxHighlighting = syntaxHighlightingIterator.next();
+    int line = lineBuilder.getLine();
+    ScannerReport.TextRange range = syntaxHighlighting.getRange();
+    if (range.getStartLine() <= line) {
+      String offsets = rangeOffsetConverter.offsetToString(syntaxHighlighting.getRange(), line, lineBuilder.getSource().length());
+      if (offsets.isEmpty()) {
+        if (range.getEndLine() == line) {
+          syntaxHighlightingIterator.remove();
+        }
+      } else {
+        if (highlighting.length() > 0) {
+          highlighting.append(SYMBOLS_SEPARATOR);
+        }
+        highlighting.append(offsets)
+          .append(OFFSET_SEPARATOR)
+          .append(getCssClass(syntaxHighlighting.getType()));
+        if (range.getEndLine() == line) {
+          syntaxHighlightingIterator.remove();
+        }
+      }
+    }
+  }
+
+  private static String getCssClass(HighlightingType type) {
+    String cssClass = cssClassByType.get(type);
+    if (cssClass != null) {
+      return cssClass;
+    } else {
+      throw new IllegalArgumentException(format("Unknown type %s ", type.toString()));
+    }
+  }
+
+  private void incrementHighlightingListMatchingLine(int line) {
+    ScannerReport.SyntaxHighlightingRule syntaxHighlighting = getNextHighlightingMatchingLine(line);
+    while (syntaxHighlighting != null) {
+      highlightingList.add(syntaxHighlighting);
+      this.currentItem = null;
+      syntaxHighlighting = getNextHighlightingMatchingLine(line);
+    }
+  }
+
+  @CheckForNull
+  private ScannerReport.SyntaxHighlightingRule getNextHighlightingMatchingLine(int line) {
+    // Get next element (if exists)
+    if (currentItem == null && lineHighlightingIterator.hasNext()) {
+      currentItem = lineHighlightingIterator.next();
+    }
+    // Return current element if lines match
+    if (currentItem != null && currentItem.getRange().getStartLine() == line) {
+      return currentItem;
+    }
+    return null;
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/LineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/LineReader.java
new file mode 100644 (file)
index 0000000..4acf9e0
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import org.sonar.db.protobuf.DbFileSources;
+
+public interface LineReader {
+
+  void read(DbFileSources.Line.Builder lineBuilder);
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/RangeOffsetConverter.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/RangeOffsetConverter.java
new file mode 100644 (file)
index 0000000..d7f1e35
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static java.lang.String.format;
+
+public class RangeOffsetConverter {
+
+  static final String OFFSET_SEPARATOR = ",";
+  static final String SYMBOLS_SEPARATOR = ";";
+
+  public String offsetToString(ScannerReport.TextRange range, int lineIndex, int lineLength) {
+    validateOffsetOrder(range, lineIndex);
+    validateStartOffsetNotGreaterThanLineLength(range, lineLength, lineIndex);
+    validateEndOffsetNotGreaterThanLineLength(range, lineLength, lineIndex);
+
+    int startOffset = range.getStartLine() == lineIndex ? range.getStartOffset() : 0;
+    int endOffset = range.getEndLine() == lineIndex ? range.getEndOffset() : lineLength;
+
+    StringBuilder element = new StringBuilder();
+    if (startOffset < endOffset) {
+      element.append(startOffset).append(OFFSET_SEPARATOR);
+      element.append(endOffset);
+    }
+
+    return element.toString();
+  }
+
+  private static void validateOffsetOrder(ScannerReport.TextRange range, int line) {
+    checkExpression(range.getStartLine() != range.getEndLine() || range.getStartOffset() <= range.getEndOffset(),
+      "End offset %s cannot be defined before start offset %s on line %s", range.getEndOffset(), range.getStartOffset(), line);
+  }
+
+  private static void validateStartOffsetNotGreaterThanLineLength(ScannerReport.TextRange range, int lineLength, int line) {
+    checkExpression(range.getStartLine() != line || range.getStartOffset() <= lineLength,
+      "Start offset %s is defined outside the length (%s) of the line %s", range.getStartOffset(), lineLength, line);
+  }
+
+  private static void validateEndOffsetNotGreaterThanLineLength(ScannerReport.TextRange range, int lineLength, int line) {
+    checkExpression(range.getEndLine() != line || range.getEndOffset() <= lineLength,
+      "End offset %s is defined outside the length (%s) of the line %s", range.getEndOffset(), lineLength, line);
+  }
+
+  private static void checkExpression(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
+    if (!expression) {
+      throw new RangeOffsetConverterException(format(errorMessageTemplate, errorMessageArgs));
+    }
+  }
+
+  public static class RangeOffsetConverterException extends RuntimeException {
+    public RangeOffsetConverterException(String message) {
+      super(message);
+    }
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReader.java
new file mode 100644 (file)
index 0000000..c2759f6
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import javax.annotation.CheckForNull;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.server.computation.task.projectanalysis.scm.Changeset;
+import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo;
+
+public class ScmLineReader implements LineReader {
+
+  private final ScmInfo scmReport;
+  @CheckForNull
+  private Changeset latestChange;
+  @CheckForNull
+  private Changeset latestChangeWithRevision;
+
+  public ScmLineReader(ScmInfo scmReport) {
+    this.scmReport = scmReport;
+  }
+
+  @Override
+  public void read(DbFileSources.Line.Builder lineBuilder) {
+    if (scmReport.hasChangesetForLine(lineBuilder.getLine())) {
+      Changeset changeset = scmReport.getChangesetForLine(lineBuilder.getLine());
+      String author = changeset.getAuthor();
+      if (author != null) {
+        lineBuilder.setScmAuthor(author);
+      }
+      String revision = changeset.getRevision();
+      if (revision != null) {
+        lineBuilder.setScmRevision(revision);
+      }
+      lineBuilder.setScmDate(changeset.getDate());
+      updateLatestChange(changeset);
+
+      if (revision != null) {
+        updateLatestChangeWithRevision(changeset);
+      }
+    }
+  }
+
+  private void updateLatestChange(Changeset newChangeSet) {
+    if (latestChange == null) {
+      latestChange = newChangeSet;
+    } else {
+      long newChangesetDate = newChangeSet.getDate();
+      long latestChangeDate = latestChange.getDate();
+      if (newChangesetDate > latestChangeDate) {
+        latestChange = newChangeSet;
+      }
+    }
+  }
+
+  private void updateLatestChangeWithRevision(Changeset newChangeSet) {
+    if (latestChangeWithRevision == null) {
+      latestChangeWithRevision = newChangeSet;
+    } else {
+      long newChangesetDate = newChangeSet.getDate();
+      long latestChangeDate = latestChangeWithRevision.getDate();
+      if (newChangesetDate > latestChangeDate) {
+        latestChangeWithRevision = newChangeSet;
+      }
+    }
+  }
+
+  @CheckForNull
+  public Changeset getLatestChangeWithRevision() {
+    return latestChangeWithRevision;
+  }
+
+  @CheckForNull
+  public Changeset getLatestChange() {
+    return latestChange;
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReader.java
new file mode 100644 (file)
index 0000000..6526d0c
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.SetMultimap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.RangeOffsetConverterException;
+
+import static java.lang.String.format;
+import static org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.OFFSET_SEPARATOR;
+import static org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.SYMBOLS_SEPARATOR;
+
+public class SymbolsLineReader implements LineReader {
+
+  private static final Logger LOG = Loggers.get(HighlightingLineReader.class);
+
+  private final Component file;
+  private final RangeOffsetConverter rangeOffsetConverter;
+  private final Map<ScannerReport.Symbol, Integer> idsBySymbol;
+  private final SetMultimap<Integer, ScannerReport.Symbol> symbolsPerLine;
+
+  private boolean areSymbolsValid = true;
+
+  public SymbolsLineReader(Component file, Iterator<ScannerReport.Symbol> symbolIterator, RangeOffsetConverter rangeOffsetConverter) {
+    this.file = file;
+    this.rangeOffsetConverter = rangeOffsetConverter;
+    List<ScannerReport.Symbol> symbols = Lists.newArrayList(symbolIterator);
+    // Sort symbols to have deterministic id generation
+    Collections.sort(symbols, SymbolsComparator.INSTANCE);
+
+    this.idsBySymbol = createIdsBySymbolMap(symbols);
+    this.symbolsPerLine = buildSymbolsPerLine(symbols);
+  }
+
+  @Override
+  public void read(DbFileSources.Line.Builder lineBuilder) {
+    if (!areSymbolsValid) {
+      return;
+    }
+    try {
+      processSymbols(lineBuilder);
+    } catch (RangeOffsetConverter.RangeOffsetConverterException e) {
+      areSymbolsValid = false;
+      LOG.warn(format("Inconsistency detected in Symbols data. Symbols will be ignored for file '%s'", file.getKey()), e);
+    }
+  }
+
+  private void processSymbols(DbFileSources.Line.Builder lineBuilder) {
+    int line = lineBuilder.getLine();
+
+    List<ScannerReport.Symbol> lineSymbols = new ArrayList<>(this.symbolsPerLine.get(line));
+    // Sort symbols to have deterministic results and avoid false variation that would lead to an unnecessary update of the source files
+    // data
+    Collections.sort(lineSymbols, SymbolsComparator.INSTANCE);
+
+    StringBuilder symbolString = new StringBuilder();
+    for (ScannerReport.Symbol lineSymbol : lineSymbols) {
+      int symbolId = idsBySymbol.get(lineSymbol);
+
+      appendSymbol(symbolString, lineSymbol.getDeclaration(), line, symbolId, lineBuilder.getSource());
+      for (ScannerReport.TextRange range : lineSymbol.getReferenceList()) {
+        appendSymbol(symbolString, range, line, symbolId, lineBuilder.getSource());
+      }
+    }
+    if (symbolString.length() > 0) {
+      lineBuilder.setSymbols(symbolString.toString());
+    }
+  }
+
+  private void appendSymbol(StringBuilder lineSymbol, ScannerReport.TextRange range, int line, int symbolId, String sourceLine) {
+    if (matchLine(range, line)) {
+      String offsets = rangeOffsetConverter.offsetToString(range, line, sourceLine.length());
+      if (!offsets.isEmpty()) {
+        if (lineSymbol.length() > 0) {
+          lineSymbol.append(SYMBOLS_SEPARATOR);
+        }
+        lineSymbol.append(offsets)
+          .append(OFFSET_SEPARATOR)
+          .append(symbolId);
+      }
+    }
+  }
+
+  private static boolean matchLine(ScannerReport.TextRange range, int line) {
+    return range.getStartLine() <= line && range.getEndLine() >= line;
+  }
+
+  private static Map<ScannerReport.Symbol, Integer> createIdsBySymbolMap(List<ScannerReport.Symbol> symbols) {
+    Map<ScannerReport.Symbol, Integer> map = new IdentityHashMap<>(symbols.size());
+    int symbolId = 1;
+    for (ScannerReport.Symbol symbol : symbols) {
+      map.put(symbol, symbolId);
+      symbolId++;
+    }
+    return map;
+  }
+
+  private static SetMultimap<Integer, ScannerReport.Symbol> buildSymbolsPerLine(List<ScannerReport.Symbol> symbols) {
+    SetMultimap<Integer, ScannerReport.Symbol> res = HashMultimap.create();
+    for (ScannerReport.Symbol symbol : symbols) {
+      putForTextRange(res, symbol, symbol.getDeclaration());
+      for (ScannerReport.TextRange textRange : symbol.getReferenceList()) {
+        putForTextRange(res, symbol, textRange);
+      }
+    }
+    return res;
+  }
+
+  private static void putForTextRange(SetMultimap<Integer, ScannerReport.Symbol> res, ScannerReport.Symbol symbol, ScannerReport.TextRange declaration) {
+    for (int i = declaration.getStartLine(); i <= declaration.getEndLine(); i++) {
+      res.put(i, symbol);
+    }
+  }
+
+  private enum SymbolsComparator implements Comparator<ScannerReport.Symbol> {
+    INSTANCE;
+
+    @Override
+    public int compare(ScannerReport.Symbol o1, ScannerReport.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());
+      }
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/package-info.java
new file mode 100644 (file)
index 0000000..8cb1be4
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import javax.annotation.ParametersAreNonnullByDefault;
index 1eede2525ee31088b26ea735ca416ec74842c079..9e5188ae0354ac340bba78c6a9ca92d7a5184db4 100644 (file)
@@ -251,7 +251,7 @@ public class NewCoverageMeasuresStep implements ComputationStep {
     @Override
     public void initialize(CounterInitializationContext context) {
       Component fileComponent = context.getLeaf();
-      Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(fileComponent);
+      java.util.Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(fileComponent);
       if (!scmInfoOptional.isPresent() || !context.hasPeriod()) {
         return;
       }
index ccd08059c467b4ecd7225e82d269ae752b2ce201..20250311c057fbfa3c4b665821ef0bbaf41cc9b7 100644 (file)
@@ -109,7 +109,7 @@ public class NewSizeMeasuresStep implements ComputationStep {
     @Override
     public void initialize(CounterInitializationContext context) {
       Component leaf = context.getLeaf();
-      Optional<ScmInfo> scmInfo = scmInfoRepository.getScmInfo(leaf);
+      java.util.Optional<ScmInfo> scmInfo = scmInfoRepository.getScmInfo(leaf);
       if (!scmInfo.isPresent() || !context.hasPeriod()) {
         return;
       }
index a7c5d4cdcde9e2c54b803bfb19e539dd7f7e75e7..2a9e3de22b8e1e57870c28cf01ec99d88d3450d8 100644 (file)
  */
 package org.sonar.server.computation.task.projectanalysis.step;
 
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.commons.codec.digest.DigestUtils;
@@ -48,14 +48,14 @@ import org.sonar.server.computation.task.projectanalysis.scm.Changeset;
 import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo;
 import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoRepository;
 import org.sonar.server.computation.task.projectanalysis.source.ComputeFileSourceData;
-import org.sonar.server.computation.task.projectanalysis.source.CoverageLineReader;
-import org.sonar.server.computation.task.projectanalysis.source.DuplicationLineReader;
-import org.sonar.server.computation.task.projectanalysis.source.HighlightingLineReader;
-import org.sonar.server.computation.task.projectanalysis.source.LineReader;
-import org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter;
-import org.sonar.server.computation.task.projectanalysis.source.ScmLineReader;
 import org.sonar.server.computation.task.projectanalysis.source.SourceLinesRepository;
-import org.sonar.server.computation.task.projectanalysis.source.SymbolsLineReader;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.CoverageLineReader;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.DuplicationLineReader;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.HighlightingLineReader;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.LineReader;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.ScmLineReader;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.SymbolsLineReader;
 import org.sonar.server.computation.task.step.ComputationStep;
 
 import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
@@ -220,10 +220,7 @@ public class PersistFileSourcesStep implements ComputationStep {
 
     @CheckForNull
     public Changeset getLatestChangeWithRevision() {
-      if (scmLineReader == null) {
-        return null;
-      }
-      return scmLineReader.getLatestChangeWithRevision();
+      return scmLineReader == null ? null : scmLineReader.getLatestChangeWithRevision();
     }
   }
 
index 2328cac62f833b25dda66667c2778803a6754e2c..d493b048a4db5ba68d2a6afc8d7864d9ca188a1b 100644 (file)
@@ -33,10 +33,12 @@ public class SourceService {
 
   private final DbClient dbClient;
   private final HtmlSourceDecorator htmlDecorator;
+  private final Function<DbFileSources.Line, String> lineToHtml;
 
   public SourceService(DbClient dbClient, HtmlSourceDecorator htmlDecorator) {
     this.dbClient = dbClient;
     this.htmlDecorator = htmlDecorator;
+    this.lineToHtml = lineToHtml();
   }
 
   /**
@@ -57,7 +59,7 @@ public class SourceService {
   }
 
   public Optional<Iterable<String>> getLinesAsHtml(DbSession dbSession, String fileUuid, int from, int toInclusive) {
-    return getLines(dbSession, fileUuid, from, toInclusive, lineToHtml());
+    return getLines(dbSession, fileUuid, from, toInclusive, lineToHtml);
   }
 
   private <E> Optional<Iterable<E>> getLines(DbSession dbSession, String fileUuid, int from, int toInclusive, Function<DbFileSources.Line, E> function) {
index 89e733a906c7dc8882439e4994c0a94718332844..ad536e6a8cc2aeb97fd239f6d659b277ee24b2fc 100644 (file)
@@ -25,7 +25,7 @@ import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.nio.charset.StandardCharsets;
-import java.util.function.Function;
+import java.util.function.Consumer;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
@@ -75,7 +75,7 @@ public class HashAction implements SourcesWsAction {
 
       response.stream().setMediaType("text/plain");
       try (OutputStreamWriter writer = new OutputStreamWriter(response.stream().output(), StandardCharsets.UTF_8)) {
-        HashFunction hashFunction = new HashFunction(writer, componentKey);
+        HashConsumer hashFunction = new HashConsumer(writer, componentKey);
         dbClient.fileSourceDao().readLineHashesStream(session, component.uuid(), hashFunction);
         if (!hashFunction.hasData()) {
           response.noContent();
@@ -84,26 +84,25 @@ public class HashAction implements SourcesWsAction {
     }
   }
 
-  private static class HashFunction implements Function<Reader, Void> {
+  private static class HashConsumer implements Consumer<Reader> {
 
     private final OutputStreamWriter writer;
     private final String componentKey;
     private boolean hasData = false;
 
-    public HashFunction(OutputStreamWriter writer, String componentKey) {
+    public HashConsumer(OutputStreamWriter writer, String componentKey) {
       this.writer = writer;
       this.componentKey = componentKey;
     }
 
     @Override
-    public Void apply(Reader input) {
+    public void accept(Reader input) {
       try {
         hasData = true;
         CharStreams.copy(input, writer);
       } catch (IOException e) {
         throw new IllegalStateException(String.format("Can't read line hashes of file '%s'", componentKey), e);
       }
-      return null;
     }
 
     public boolean hasData() {
index 2314d14056e588895f7554744c3997e1959f0747..cde7eea7a1b26a6aeea4df391d2bf72a362d8cdc 100644 (file)
@@ -363,7 +363,7 @@ public class IssueCreationDateCalculatorTest {
 
   private void noScm() {
     when(scmInfoRepository.getScmInfo(component))
-      .thenReturn(Optional.absent());
+      .thenReturn(java.util.Optional.empty());
   }
 
   private void withScm(long blame) {
@@ -376,7 +376,7 @@ public class IssueCreationDateCalculatorTest {
     if (scmInfo == null) {
       scmInfo = mock(ScmInfo.class);
       when(scmInfoRepository.getScmInfo(component))
-        .thenReturn(Optional.of(scmInfo));
+        .thenReturn(java.util.Optional.empty());
     }
   }
 
index bd1845e4832f734d3631946ee549e3babd21f331..a231f5512d62f7edb6b4afbea867b0cf4daa262f 100644 (file)
@@ -83,7 +83,7 @@ public class ScmInfoDbLoaderTest {
     assertThat(scmInfo.getAllChangesets()).hasSize(1);
     assertThat(scmInfo.fileHash()).isEqualTo(hash);
 
-    assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from db for file 'FILE_UUID'");
+    assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'FILE_UUID'");
   }
 
   @Test
@@ -101,7 +101,7 @@ public class ScmInfoDbLoaderTest {
     DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
     assertThat(scmInfo.getAllChangesets()).hasSize(1);
     assertThat(scmInfo.fileHash()).isEqualTo(hash);
-    assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from db for file 'mergeFileUuid'");
+    assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'mergeFileUuid'");
   }
   
   @Test
@@ -111,7 +111,7 @@ public class ScmInfoDbLoaderTest {
     
     Optional<DbScmInfo> scmInfo = underTest.getScmInfo(FILE);
     
-    assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from db for file 'FILE_UUID'");
+    assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'FILE_UUID'");
     assertThat(scmInfo).isEmpty();
   }
 
index aa6522f5a31dfc6f5c376eae3dbb9f3f9b417f88..b66f5b2281e0393057116bcf951f3515d16d9d54 100644 (file)
@@ -50,7 +50,6 @@ import org.sonar.server.computation.task.projectanalysis.source.SourceHashReposi
 import org.sonar.server.computation.task.projectanalysis.source.SourceLinesDiff;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.guava.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -93,7 +92,7 @@ public class ScmInfoRepositoryImplTest {
   public void return_empty_if_component_is_not_file() {
     Component c = mock(Component.class);
     when(c.getType()).thenReturn(Type.DIRECTORY);
-    assertThat(underTest.getScmInfo(c)).isAbsent();
+    assertThat(underTest.getScmInfo(c)).isEmpty();
   }
 
   @Test
@@ -205,7 +204,7 @@ public class ScmInfoRepositoryImplTest {
   @Test
   public void generate_scm_info_for_new_and_changed_lines_when_report_is_empty() {
     createDbScmInfoWithOneLine("hash");
-    when(diff.getMatchingLines(FILE)).thenReturn(new int[] {1, 0, 0});
+    when(diff.computeMatchingLines(FILE)).thenReturn(new int[] {1, 0, 0});
     addFileSourceInReport(3);
     ScmInfo scmInfo = underTest.getScmInfo(FILE).get();
     assertThat(scmInfo.getAllChangesets()).hasSize(3);
@@ -215,7 +214,7 @@ public class ScmInfoRepositoryImplTest {
     assertChangeset(scmInfo.getChangesetForLine(3), null, null, analysisDate.getTime());
 
     verify(dbLoader).getScmInfo(FILE);
-    verify(diff).getMatchingLines(FILE);
+    verify(diff).computeMatchingLines(FILE);
     verifyNoMoreInteractions(dbLoader);
     verifyZeroInteractions(sourceHashRepository);
     verifyNoMoreInteractions(diff);
@@ -235,7 +234,7 @@ public class ScmInfoRepositoryImplTest {
     BatchReportReader batchReportReader = mock(BatchReportReader.class);
     ScmInfoRepositoryImpl underTest = new ScmInfoRepositoryImpl(batchReportReader, analysisMetadata, dbLoader, diff, sourceHashRepository);
 
-    assertThat(underTest.getScmInfo(component)).isAbsent();
+    assertThat(underTest.getScmInfo(component)).isEmpty();
 
     verifyZeroInteractions(batchReportReader, dbLoader);
   }
index 0c42e7bde7127b26be4fc05aaf8cf02fa84a0424..613ad85f791e61c907c15703929c5224182ffe9d 100644 (file)
@@ -19,9 +19,9 @@
  */
 package org.sonar.server.computation.task.projectanalysis.scm;
 
-import com.google.common.base.Optional;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 import org.junit.rules.ExternalResource;
@@ -42,7 +42,7 @@ public class ScmInfoRepositoryRule extends ExternalResource implements ScmInfoRe
   public Optional<ScmInfo> getScmInfo(Component component) {
     checkNotNull(component, "Component cannot be bull");
     ScmInfo scmInfo = scmInfoByFileRef.get(component.getReportAttributes().getRef());
-    return Optional.fromNullable(scmInfo);
+    return Optional.ofNullable(scmInfo);
   }
 
   public ScmInfoRepositoryRule setScmInfo(int fileRef, Changeset... changesetList) {
index a0eb78a925d9c24fb072addfeafd936147422306..c6282f6dcc8e2ba53e3fd83572d5238209fdc457 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.computation.task.projectanalysis.source;
 import com.google.common.collect.Lists;
 import org.junit.Test;
 import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.LineReader;
 
 import static com.google.common.collect.Lists.newArrayList;
 import static org.assertj.core.api.Assertions.assertThat;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReaderTest.java
deleted file mode 100644 (file)
index fa43ff2..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import java.util.Collections;
-import org.junit.Test;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.scanner.protocol.output.ScannerReport;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class CoverageLineReaderTest {
-
-  @Test
-  public void set_coverage() {
-    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder()
-      .setLine(1)
-      .setConditions(10)
-      .setHits(true)
-      .setCoveredConditions(2)
-      .build()).iterator());
-
-    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
-    computeCoverageLine.read(lineBuilder);
-
-    assertThat(lineBuilder.getLineHits()).isEqualTo(1);
-    assertThat(lineBuilder.getConditions()).isEqualTo(10);
-    assertThat(lineBuilder.getCoveredConditions()).isEqualTo(2);
-  }
-
-  // Some tools are only able to report condition coverage
-  @Test
-  public void set_coverage_only_conditions() {
-    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder()
-      .setLine(1)
-      .setConditions(10)
-      .setCoveredConditions(2)
-      .build()).iterator());
-
-    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
-    computeCoverageLine.read(lineBuilder);
-
-    assertThat(lineBuilder.hasLineHits()).isFalse();
-    assertThat(lineBuilder.getConditions()).isEqualTo(10);
-  }
-
-  @Test
-  public void set_coverage_on_uncovered_lines() {
-    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder()
-      .setLine(1)
-      .setHits(false)
-      .build()).iterator());
-
-    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
-    computeCoverageLine.read(lineBuilder);
-
-    assertThat(lineBuilder.hasLineHits()).isTrue();
-    assertThat(lineBuilder.getLineHits()).isEqualTo(0);
-  }
-
-  @Test
-  public void nothing_to_do_when_no_coverage_info() {
-    CoverageLineReader computeCoverageLine = new CoverageLineReader(Collections.<ScannerReport.LineCoverage>emptyList().iterator());
-
-    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
-    computeCoverageLine.read(lineBuilder);
-
-    assertThat(lineBuilder.hasLineHits()).isFalse();
-    assertThat(lineBuilder.hasConditions()).isFalse();
-    assertThat(lineBuilder.hasCoveredConditions()).isFalse();
-  }
-
-  @Test
-  public void nothing_to_do_when_no_coverage_info_for_current_line() {
-    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(
-      ScannerReport.LineCoverage.newBuilder()
-        .setLine(1)
-        .setConditions(10)
-        .setHits(true)
-        .setCoveredConditions(2)
-        .build()
-    // No coverage info on line 2
-    ).iterator());
-
-    DbFileSources.Line.Builder line2Builder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(2);
-    computeCoverageLine.read(line2Builder);
-
-    assertThat(line2Builder.hasLineHits()).isFalse();
-    assertThat(line2Builder.hasConditions()).isFalse();
-    assertThat(line2Builder.hasCoveredConditions()).isFalse();
-  }
-
-  @Test
-  public void nothing_to_do_when_no_coverage_info_for_next_line() {
-    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(
-      ScannerReport.LineCoverage.newBuilder()
-        .setLine(1)
-        .setConditions(10)
-        .setHits(true)
-        .setCoveredConditions(2)
-        .build()
-    // No coverage info on line 2
-    ).iterator());
-
-    DbFileSources.Data.Builder fileSourceBuilder = DbFileSources.Data.newBuilder();
-    DbFileSources.Line.Builder line1Builder = fileSourceBuilder.addLinesBuilder().setLine(1);
-    DbFileSources.Line.Builder line2Builder = fileSourceBuilder.addLinesBuilder().setLine(2);
-    computeCoverageLine.read(line1Builder);
-    computeCoverageLine.read(line2Builder);
-
-    assertThat(line2Builder.hasLineHits()).isFalse();
-    assertThat(line2Builder.hasConditions()).isFalse();
-    assertThat(line2Builder.hasCoveredConditions()).isFalse();
-  }
-
-  @Test
-  public void does_not_set_deprecated_coverage_fields() {
-    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder()
-      .setLine(1)
-      .setConditions(10)
-      .setHits(true)
-      .setCoveredConditions(2)
-      .build()).iterator());
-
-    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
-    computeCoverageLine.read(lineBuilder);
-
-    assertThat(lineBuilder.hasDeprecatedUtLineHits()).isFalse();
-    assertThat(lineBuilder.hasDeprecatedUtConditions()).isFalse();
-    assertThat(lineBuilder.hasDeprecatedUtCoveredConditions()).isFalse();
-    assertThat(lineBuilder.hasDeprecatedOverallLineHits()).isFalse();
-    assertThat(lineBuilder.hasDeprecatedOverallConditions()).isFalse();
-    assertThat(lineBuilder.hasDeprecatedOverallCoveredConditions()).isFalse();
-    assertThat(lineBuilder.hasDeprecatedItLineHits()).isFalse();
-    assertThat(lineBuilder.hasDeprecatedItConditions()).isFalse();
-    assertThat(lineBuilder.hasDeprecatedItCoveredConditions()).isFalse();
-  }
-
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReaderTest.java
deleted file mode 100644 (file)
index 3aa0886..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.Arrays;
-import java.util.Collections;
-import org.junit.Test;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
-import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
-import org.sonar.server.computation.task.projectanalysis.duplication.CrossProjectDuplicate;
-import org.sonar.server.computation.task.projectanalysis.duplication.Duplicate;
-import org.sonar.server.computation.task.projectanalysis.duplication.Duplication;
-import org.sonar.server.computation.task.projectanalysis.duplication.InProjectDuplicate;
-import org.sonar.server.computation.task.projectanalysis.duplication.InnerDuplicate;
-import org.sonar.server.computation.task.projectanalysis.duplication.TextBlock;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class DuplicationLineReaderTest {
-
-  DbFileSources.Data.Builder sourceData = DbFileSources.Data.newBuilder();
-  DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1);
-  DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2);
-  DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3);
-  DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4);
-
-  @Test
-  public void read_nothing() {
-    DuplicationLineReader reader = new DuplicationLineReader(Collections.emptySet());
-
-    reader.read(line1);
-
-    assertThat(line1.getDuplicationList()).isEmpty();
-  }
-
-  @Test
-  public void read_duplication_with_duplicates_on_same_file() {
-    DuplicationLineReader reader = duplicationLineReader(duplication(1, 2, innerDuplicate(3, 4)));
-
-    reader.read(line1);
-    reader.read(line2);
-    reader.read(line3);
-    reader.read(line4);
-
-    assertThat(line1.getDuplicationList()).containsExactly(1);
-    assertThat(line2.getDuplicationList()).containsExactly(1);
-    assertThat(line3.getDuplicationList()).containsExactly(2);
-    assertThat(line4.getDuplicationList()).containsExactly(2);
-  }
-
-  @Test
-  public void read_duplication_with_duplicates_on_other_file() {
-    DuplicationLineReader reader = duplicationLineReader(
-        duplication(
-            1, 2,
-            new InProjectDuplicate(fileComponent(1).build(), new TextBlock(3, 4))));
-
-    reader.read(line1);
-    reader.read(line2);
-    reader.read(line3);
-    reader.read(line4);
-
-    assertThat(line1.getDuplicationList()).containsExactly(1);
-    assertThat(line2.getDuplicationList()).containsExactly(1);
-    assertThat(line3.getDuplicationList()).isEmpty();
-    assertThat(line4.getDuplicationList()).isEmpty();
-  }
-
-  @Test
-  public void read_duplication_with_duplicates_on_other_file_from_other_project() {
-    DuplicationLineReader reader = duplicationLineReader(
-        duplication(
-            1, 2,
-            new CrossProjectDuplicate("other-component-key-from-another-project", new TextBlock(3, 4))));
-
-    reader.read(line1);
-    reader.read(line2);
-    reader.read(line3);
-    reader.read(line4);
-
-    assertThat(line1.getDuplicationList()).containsExactly(1);
-    assertThat(line2.getDuplicationList()).containsExactly(1);
-    assertThat(line3.getDuplicationList()).isEmpty();
-    assertThat(line4.getDuplicationList()).isEmpty();
-  }
-
-  @Test
-  public void read_many_duplications() {
-    DuplicationLineReader reader = duplicationLineReader(
-        duplication(
-            1, 1,
-            innerDuplicate(2, 2)),
-        duplication(
-            1, 2,
-            innerDuplicate(3, 4))
-    );
-
-    reader.read(line1);
-    reader.read(line2);
-    reader.read(line3);
-    reader.read(line4);
-
-    assertThat(line1.getDuplicationList()).containsExactly(1, 2);
-    assertThat(line2.getDuplicationList()).containsExactly(2, 3);
-    assertThat(line3.getDuplicationList()).containsExactly(4);
-    assertThat(line4.getDuplicationList()).containsExactly(4);
-  }
-
-  @Test
-  public void should_be_sorted_by_line_block() {
-    DuplicationLineReader reader = duplicationLineReader(
-        duplication(
-            2, 2,
-            innerDuplicate(4, 4)),
-        duplication(
-            1, 1,
-            innerDuplicate(3, 3))
-    );
-
-    reader.read(line1);
-    reader.read(line2);
-    reader.read(line3);
-    reader.read(line4);
-
-    assertThat(line1.getDuplicationList()).containsExactly(1);
-    assertThat(line2.getDuplicationList()).containsExactly(2);
-    assertThat(line3.getDuplicationList()).containsExactly(3);
-    assertThat(line4.getDuplicationList()).containsExactly(4);
-  }
-
-  @Test
-  public void should_be_sorted_by_line_length() {
-    DuplicationLineReader reader = duplicationLineReader(
-        duplication(
-            1, 2,
-            innerDuplicate(3, 4)),
-        duplication(
-            1, 1,
-            innerDuplicate(4, 4)
-        )
-    );
-
-    reader.read(line1);
-    reader.read(line2);
-    reader.read(line3);
-    reader.read(line4);
-
-    assertThat(line1.getDuplicationList()).containsExactly(1, 2);
-    assertThat(line2.getDuplicationList()).containsExactly(2);
-    assertThat(line3.getDuplicationList()).containsExactly(3);
-    assertThat(line4.getDuplicationList()).containsExactly(3, 4);
-  }
-
-  private static ReportComponent.Builder fileComponent(int ref) {
-    return ReportComponent.builder(Component.Type.FILE, ref);
-  }
-
-  private static DuplicationLineReader duplicationLineReader(Duplication... duplications) {
-    return new DuplicationLineReader(ImmutableSet.copyOf(Arrays.asList(duplications)));
-  }
-
-  private static Duplication duplication(int originalStart, int originalEnd, Duplicate... duplicates) {
-    return new Duplication(new TextBlock(originalStart, originalEnd), Arrays.asList(duplicates));
-  }
-
-  private static InnerDuplicate innerDuplicate(int start, int end) {
-    return new InnerDuplicate(new TextBlock(start, end));
-  }
-
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReaderTest.java
deleted file mode 100644 (file)
index 090e9bd..0000000
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.scanner.protocol.output.ScannerReport;
-import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType;
-import org.sonar.scanner.protocol.output.ScannerReport.TextRange;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
-import org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.RangeOffsetConverterException;
-
-import static com.google.common.collect.ImmutableMap.of;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.utils.log.LoggerLevel.WARN;
-import static org.sonar.db.protobuf.DbFileSources.Data.newBuilder;
-import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.ANNOTATION;
-import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.COMMENT;
-import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.CONSTANT;
-import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.CPP_DOC;
-import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.HIGHLIGHTING_STRING;
-import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.KEYWORD;
-import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
-
-public class HighlightingLineReaderTest {
-
-  @Rule
-  public LogTester logTester = new LogTester();
-
-  private static final Component FILE = builder(Component.Type.FILE, 1).setUuid("FILE_UUID").setKey("FILE_KEY").build();
-
-  private static final int DEFAULT_LINE_LENGTH = 5;
-
-  private static final int LINE_1 = 1;
-  private static final int LINE_2 = 2;
-  private static final int LINE_3 = 3;
-  private static final int LINE_4 = 4;
-
-  private static final String RANGE_LABEL_1 = "1,2";
-  private static final String RANGE_LABEL_2 = "2,3";
-  private static final String RANGE_LABEL_3 = "3,4";
-  private static final String RANGE_LABEL_4 = "0,2";
-  private static final String RANGE_LABEL_5 = "0,3";
-
-  private RangeOffsetConverter rangeOffsetConverter = mock(RangeOffsetConverter.class);
-
-  private DbFileSources.Data.Builder sourceData = newBuilder();
-  private DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1);
-  private DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2);
-  private DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3);
-  private DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4);
-
-  @Test
-  public void nothing_to_read() {
-    HighlightingLineReader highlightingLineReader = newReader(Collections.emptyMap());
-
-    DbFileSources.Line.Builder lineBuilder = newBuilder().addLinesBuilder().setLine(1);
-    highlightingLineReader.read(lineBuilder);
-
-    assertThat(lineBuilder.hasHighlighting()).isFalse();
-  }
-
-  @Test
-  public void read_one_line() {
-    HighlightingLineReader highlightingLineReader = newReader(of(
-      newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION));
-
-    highlightingLineReader.read(line1);
-
-    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
-  }
-
-  @Test
-  public void read_many_lines() {
-    HighlightingLineReader highlightingLineReader = newReader(of(
-      newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
-      newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_2), COMMENT,
-      newSingleLineTextRangeWithExpectingLabel(LINE_4, RANGE_LABEL_3), CONSTANT));
-
-    highlightingLineReader.read(line1);
-    highlightingLineReader.read(line2);
-    highlightingLineReader.read(line3);
-    highlightingLineReader.read(line4);
-
-    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
-    assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",cd");
-    assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",c");
-  }
-
-  @Test
-  public void supports_highlighting_over_multiple_lines_including_an_empty_one() {
-    List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList = new ArrayList<>();
-    addHighlighting(syntaxHighlightingList, 1, 0, 1, 7, KEYWORD); // package
-    addHighlighting(syntaxHighlightingList, 2, 0, 4, 6, CPP_DOC); // comment over 3 lines
-    addHighlighting(syntaxHighlightingList, 5, 0, 5, 6, KEYWORD); // public
-    addHighlighting(syntaxHighlightingList, 5, 7, 5, 12, KEYWORD); // class
-    HighlightingLineReader highlightingLineReader = new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), new RangeOffsetConverter());
-
-    DbFileSources.Line.Builder[] builders = new DbFileSources.Line.Builder[] {
-      addSourceLine(highlightingLineReader, 1, "package example;"),
-      addSourceLine(highlightingLineReader, 2, "/*"),
-      addSourceLine(highlightingLineReader, 3, ""),
-      addSourceLine(highlightingLineReader, 4, " foo*/"),
-      addSourceLine(highlightingLineReader, 5, "public class One {"),
-      addSourceLine(highlightingLineReader, 6, "}")
-    };
-
-    assertThat(builders)
-      .extracting("highlighting")
-      .containsExactly(
-        "0,7,k",
-        "0,2,cppd",
-        "",
-        "0,6,cppd",
-        "0,6,k;7,12,k",
-        "");
-  }
-
-  private DbFileSources.Line.Builder addSourceLine(HighlightingLineReader highlightingLineReader, int line, String source) {
-    DbFileSources.Line.Builder lineBuilder = sourceData.addLinesBuilder().setSource(source).setLine(line);
-    highlightingLineReader.read(lineBuilder);
-    return lineBuilder;
-  }
-
-  private void addHighlighting(List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList,
-    int startLine, int startOffset,
-    int endLine, int endOffset,
-    HighlightingType type) {
-    TextRange.Builder textRangeBuilder = TextRange.newBuilder();
-    ScannerReport.SyntaxHighlightingRule.Builder ruleBuilder = ScannerReport.SyntaxHighlightingRule.newBuilder();
-    syntaxHighlightingList.add(ruleBuilder
-      .setRange(textRangeBuilder
-        .setStartLine(startLine).setEndLine(endLine)
-        .setStartOffset(startOffset).setEndOffset(endOffset)
-        .build())
-      .setType(type)
-      .build());
-  }
-
-  @Test
-  public void read_many_syntax_highlighting_on_same_line() {
-    HighlightingLineReader highlightingLineReader = newReader(of(
-      newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
-      newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_2), COMMENT));
-
-    highlightingLineReader.read(line1);
-
-    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a;" + RANGE_LABEL_2 + ",cd");
-  }
-
-  @Test
-  public void read_one_syntax_highlighting_on_many_lines() {
-    // This highlighting begin on line 1 and finish on line 3
-    TextRange textRange = newTextRange(LINE_1, LINE_3);
-    when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
-    when(rangeOffsetConverter.offsetToString(textRange, LINE_2, 6)).thenReturn(RANGE_LABEL_2);
-    when(rangeOffsetConverter.offsetToString(textRange, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3);
-
-    HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION));
-
-    highlightingLineReader.read(line1);
-    DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line 2").setLine(2);
-    highlightingLineReader.read(line2);
-    highlightingLineReader.read(line3);
-
-    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
-    assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a");
-    assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a");
-  }
-
-  @Test
-  public void read_many_syntax_highlighting_on_many_lines() {
-    TextRange textRange1 = newTextRange(LINE_1, LINE_3);
-    when(rangeOffsetConverter.offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
-    when(rangeOffsetConverter.offsetToString(textRange1, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
-    when(rangeOffsetConverter.offsetToString(textRange1, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3);
-
-    TextRange textRange2 = newTextRange(LINE_2, LINE_4);
-    when(rangeOffsetConverter.offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
-    when(rangeOffsetConverter.offsetToString(textRange2, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
-    when(rangeOffsetConverter.offsetToString(textRange2, LINE_4, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_4);
-
-    TextRange textRange3 = newTextRange(LINE_2, LINE_2);
-    when(rangeOffsetConverter.offsetToString(textRange3, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_5);
-
-    HighlightingLineReader highlightingLineReader = newReader(of(
-      textRange1, ANNOTATION,
-      textRange2, HIGHLIGHTING_STRING,
-      textRange3, COMMENT));
-
-    highlightingLineReader.read(line1);
-    highlightingLineReader.read(line2);
-    highlightingLineReader.read(line3);
-    highlightingLineReader.read(line4);
-
-    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
-    assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a;" + RANGE_LABEL_2 + ",s;" + RANGE_LABEL_5 + ",cd");
-    assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a;" + RANGE_LABEL_2 + ",s");
-    assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_4 + ",s");
-  }
-
-  @Test
-  public void read_highlighting_declared_on_a_whole_line() {
-    TextRange textRange = newTextRange(LINE_1, LINE_2);
-    when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
-    when(rangeOffsetConverter.offsetToString(textRange, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn("");
-
-    HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION));
-
-    highlightingLineReader.read(line1);
-    highlightingLineReader.read(line2);
-    highlightingLineReader.read(line3);
-
-    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
-    // Nothing should be set on line 2
-    assertThat(line2.getHighlighting()).isEmpty();
-    assertThat(line3.getHighlighting()).isEmpty();
-  }
-
-  @Test
-  public void not_fail_and_stop_processing_when_range_offset_converter_throw_RangeOffsetConverterException() {
-    TextRange textRange1 = newTextRange(LINE_1, LINE_1);
-    doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH);
-
-    HighlightingLineReader highlightingLineReader = newReader(of(
-      textRange1, HighlightingType.ANNOTATION,
-      newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_1), HIGHLIGHTING_STRING));
-
-    highlightingLineReader.read(line1);
-    highlightingLineReader.read(line2);
-
-    assertNoHighlighting();
-    assertThat(logTester.logs(WARN)).isNotEmpty();
-  }
-
-  @Test
-  public void keep_existing_processed_highlighting_when_range_offset_converter_throw_RangeOffsetConverterException() {
-    TextRange textRange2 = newTextRange(LINE_2, LINE_2);
-    doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH);
-
-    HighlightingLineReader highlightingLineReader = newReader(of(
-      newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
-      textRange2, HIGHLIGHTING_STRING));
-
-    highlightingLineReader.read(line1);
-    highlightingLineReader.read(line2);
-
-    assertThat(line1.hasHighlighting()).isTrue();
-    assertThat(line2.hasHighlighting()).isFalse();
-    assertThat(logTester.logs(WARN)).isNotEmpty();
-  }
-
-  @Test
-  public void display_file_key_in_warning_when_range_offset_converter_throw_RangeOffsetConverterException() {
-    TextRange textRange1 = newTextRange(LINE_1, LINE_1);
-    doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH);
-    HighlightingLineReader highlightingLineReader = newReader(of(textRange1, ANNOTATION));
-
-    highlightingLineReader.read(line1);
-
-    assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Highlighting data. Highlighting will be ignored for file 'FILE_KEY'");
-  }
-
-  private HighlightingLineReader newReader(Map<TextRange, HighlightingType> textRangeByType) {
-    List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList = new ArrayList<>();
-    for (Map.Entry<TextRange, HighlightingType> entry : textRangeByType.entrySet()) {
-      syntaxHighlightingList.add(ScannerReport.SyntaxHighlightingRule.newBuilder()
-        .setRange(entry.getKey())
-        .setType(entry.getValue())
-        .build());
-    }
-    return new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), rangeOffsetConverter);
-  }
-
-  private static TextRange newTextRange(int startLine, int enLine) {
-    Random random = new Random();
-    return TextRange.newBuilder()
-      .setStartLine(startLine).setEndLine(enLine)
-      // Offsets are not used by the reader
-      .setStartOffset(random.nextInt()).setEndOffset(random.nextInt())
-      .build();
-  }
-
-  private TextRange newSingleLineTextRangeWithExpectingLabel(int line, String rangeLabel) {
-    TextRange textRange = newTextRange(line, line);
-    when(rangeOffsetConverter.offsetToString(textRange, line, DEFAULT_LINE_LENGTH)).thenReturn(rangeLabel);
-    return textRange;
-  }
-
-  private void assertNoHighlighting() {
-    assertThat(line1.hasHighlighting()).isFalse();
-    assertThat(line2.hasHighlighting()).isFalse();
-    assertThat(line3.hasHighlighting()).isFalse();
-    assertThat(line4.hasHighlighting()).isFalse();
-  }
-
-}
index 99c03ad4317047d09c96480e60155eac07bd4f0d..88c19784e190eed19f50ba596575a2bdfef42237 100644 (file)
@@ -23,7 +23,8 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.scanner.protocol.output.ScannerReport;
-import org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.RangeOffsetConverterException;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.RangeOffsetConverterException;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReaderTest.java
deleted file mode 100644 (file)
index 1abd955..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import com.google.common.collect.ImmutableMap;
-import java.util.Collections;
-import java.util.Map;
-import org.junit.Test;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.server.computation.task.projectanalysis.scm.Changeset;
-import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo;
-import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoImpl;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ScmLineReaderTest {
-
-  @Test
-  public void set_scm() {
-    ScmInfo scmInfo = new ScmInfoImpl(Collections.singletonMap(1,
-      Changeset.newChangesetBuilder()
-        .setAuthor("john")
-        .setDate(123_456_789L)
-        .setRevision("rev-1")
-        .build()));
-
-    ScmLineReader lineScm = new ScmLineReader(scmInfo);
-
-    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
-    lineScm.read(lineBuilder);
-
-    assertThat(lineBuilder.getScmAuthor()).isEqualTo("john");
-    assertThat(lineBuilder.getScmDate()).isEqualTo(123_456_789L);
-    assertThat(lineBuilder.getScmRevision()).isEqualTo("rev-1");
-  }
-
-  @Test
-  public void set_scm_with_minim_fields() {
-    ScmInfo scmInfo = new ScmInfoImpl(Collections.singletonMap(1,
-      Changeset.newChangesetBuilder()
-        .setDate(123456789L)
-        .build()));
-
-    ScmLineReader lineScm = new ScmLineReader(scmInfo);
-
-    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
-    lineScm.read(lineBuilder);
-
-    assertThat(lineBuilder.hasScmAuthor()).isFalse();
-    assertThat(lineBuilder.getScmDate()).isEqualTo(123456789L);
-    assertThat(lineBuilder.hasScmRevision()).isFalse();
-
-  }
-
-  @Test
-  public void getLatestChange_returns_changeset_with_highest_date_of_read_lines() {
-    long refDate = 123_456_789L;
-    Changeset changeset0 = Changeset.newChangesetBuilder().setDate(refDate - 636).setRevision("rev-1").build();
-    Changeset changeset1 = Changeset.newChangesetBuilder().setDate(refDate + 1).setRevision("rev-2").build();
-    Changeset changeset2 = Changeset.newChangesetBuilder().setDate(refDate + 2).build();
-    ScmInfo scmInfo = new ScmInfoImpl(setup8LinesChangeset(changeset0, changeset1, changeset2));
-
-    ScmLineReader lineScm = new ScmLineReader(scmInfo);
-
-    // before any line is read, the latest changes are null
-    assertThat(lineScm.getLatestChange()).isNull();
-    assertThat(lineScm.getLatestChangeWithRevision()).isNull();
-
-    // read line 1, only one changeset => 0
-    readLineAndAssertLatestChanges(lineScm, 1, changeset0, changeset0);
-
-    // read line 2, latest changeset is 1
-    readLineAndAssertLatestChanges(lineScm, 2, changeset1, changeset1);
-
-    // read line 3, latest changeset is still 1
-    readLineAndAssertLatestChanges(lineScm, 3, changeset1, changeset1);
-
-    // read line 4, latest changeset is now 2
-    readLineAndAssertLatestChanges(lineScm, 4, changeset2, changeset1);
-
-    // read line 5 to 8, there will never be any changeset more recent than 2
-    readLineAndAssertLatestChanges(lineScm, 5, changeset2, changeset1);
-    readLineAndAssertLatestChanges(lineScm, 6, changeset2, changeset1);
-    readLineAndAssertLatestChanges(lineScm, 7, changeset2, changeset1);
-    readLineAndAssertLatestChanges(lineScm, 8, changeset2, changeset1);
-  }
-
-  private static Map<Integer, Changeset> setup8LinesChangeset(Changeset changeset0, Changeset changeset1, Changeset changeset2) {
-    return ImmutableMap.<Integer, Changeset>builder()
-      .put(1, changeset0)
-      .put(2, changeset1)
-      .put(3, changeset1)
-      .put(4, changeset2)
-      .put(5, changeset0)
-      .put(6, changeset1)
-      .put(7, changeset0)
-      .put(8, changeset0).build();
-  }
-
-  private void readLineAndAssertLatestChanges(ScmLineReader lineScm, int line, Changeset expectedChangeset, Changeset expectedChangesetWithRevision) {
-    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(line);
-    lineScm.read(lineBuilder);
-    assertThat(lineScm.getLatestChange()).isSameAs(expectedChangeset);
-    assertThat(lineScm.getLatestChangeWithRevision()).isSameAs(expectedChangesetWithRevision);
-
-  }
-
-}
index 99394391a66281326bc9b8e210f3b9b5d498c613..7fe94520fa5d8d55edbe520fcff41589f54952d9 100644 (file)
@@ -43,7 +43,7 @@ public class SourceLinesDiffFinderTest {
     report.add("line - 3");
     report.add("line - 4");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
 
     assertThat(diff).containsExactly(1, 2, 3, 4, 5);
   }
@@ -74,7 +74,7 @@ public class SourceLinesDiffFinderTest {
     report.add("    }\n");
     report.add("}\n");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
     assertThat(diff).containsExactly(1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 5, 6, 7);
   }
 
@@ -92,7 +92,7 @@ public class SourceLinesDiffFinderTest {
     report.add("line - 2");
     report.add("line - 3");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
 
     assertThat(diff).containsExactly(0, 0, 3, 4);
   }
@@ -111,7 +111,7 @@ public class SourceLinesDiffFinderTest {
     report.add("line - 2 - modified");
     report.add("line - 3 - modified");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
 
     assertThat(diff).containsExactly(1, 2, 0, 0);
   }
@@ -134,7 +134,7 @@ public class SourceLinesDiffFinderTest {
     report.add("line - 4");
     report.add("line - 5");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
 
     assertThat(diff).containsExactly(1, 2, 0, 0, 5, 6);
   }
@@ -153,7 +153,7 @@ public class SourceLinesDiffFinderTest {
     report.add("line - 1");
     report.add("line - 2");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
 
     assertThat(diff).containsExactly(0, 0, 1, 2, 3);
   }
@@ -174,7 +174,7 @@ public class SourceLinesDiffFinderTest {
     report.add("line - 2");
     report.add("line - 3");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
 
     assertThat(diff).containsExactly(1, 2, 0, 0, 3, 4);
   }
@@ -193,7 +193,7 @@ public class SourceLinesDiffFinderTest {
     report.add("line - new");
     report.add("line - new");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
 
     assertThat(diff).containsExactly(1, 2, 3, 0, 0);
   }
@@ -212,7 +212,7 @@ public class SourceLinesDiffFinderTest {
     report.add("line - 1");
     report.add("line - 2");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
 
     assertThat(diff).containsExactly(1, 2, 3);
   }
@@ -233,7 +233,7 @@ public class SourceLinesDiffFinderTest {
     report.add("line - 4");
     report.add("line - 5");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
 
     assertThat(diff).containsExactly(1, 2, 5, 6);
   }
@@ -250,7 +250,7 @@ public class SourceLinesDiffFinderTest {
     report.add("line - 2");
     report.add("line - 3");
 
-    int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines();
+    int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report);
 
     assertThat(diff).containsExactly(3, 4);
   }
index 1bd89561ef5b7e405ff63b3a6b9cd599ac05197f..c692c663686282fc47d81eee2afb2bea8a7cba1f 100644 (file)
@@ -82,7 +82,7 @@ public class SourceLinesDiffImplTest {
     setFileContentInReport(FILE_REF, CONTENT);
 
     Component component = fileComponent(FILE_REF);
-    assertThat(underTest.getMatchingLines(component)).containsExactly(1, 2, 3, 4, 5, 6, 7);
+    assertThat(underTest.computeMatchingLines(component)).containsExactly(1, 2, 3, 4, 5, 6, 7);
 
   }
 
index c59a71008203d319b47eb059e41c9c926b9673c1..1dacd1fe8dee97b51518633b9074eaaf6ac8d66a 100644 (file)
@@ -92,7 +92,7 @@ public class SourceLinesRepositoryImplTest {
   @Test
   public void fail_with_NPE_to_read_lines_on_null_component() {
     thrown.expect(NullPointerException.class);
-    thrown.expectMessage("Component should not be bull");
+    thrown.expectMessage("Component should not be null");
 
     underTest.readLines(null);
   }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReaderTest.java
deleted file mode 100644 (file)
index e9b69da..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.source;
-
-import java.util.Arrays;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.scanner.protocol.output.ScannerReport;
-import org.sonar.scanner.protocol.output.ScannerReport.TextRange;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.utils.log.LoggerLevel.WARN;
-import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
-
-public class SymbolsLineReaderTest {
-
-  @Rule
-  public LogTester logTester = new LogTester();
-
-  private static final Component FILE = builder(Component.Type.FILE, 1).setUuid("FILE_UUID").setKey("FILE_KEY").build();
-
-  private static final int DEFAULT_LINE_LENGTH = 5;
-
-  private static final int LINE_1 = 1;
-  private static final int LINE_2 = 2;
-  private static final int LINE_3 = 3;
-  private static final int LINE_4 = 4;
-
-  private static final int OFFSET_0 = 0;
-  private static final int OFFSET_1 = 1;
-  private static final int OFFSET_2 = 2;
-  private static final int OFFSET_3 = 3;
-  private static final int OFFSET_4 = 4;
-
-  private static final String RANGE_LABEL_1 = "1,2";
-  private static final String RANGE_LABEL_2 = "2,3";
-  private static final String RANGE_LABEL_3 = "3,4";
-  private static final String RANGE_LABEL_4 = "0,2";
-
-  private RangeOffsetConverter rangeOffsetConverter = mock(RangeOffsetConverter.class);
-
-  private DbFileSources.Data.Builder sourceData = DbFileSources.Data.newBuilder();
-  private DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1);
-  private DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2);
-  private DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3);
-  private DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4);
-
-  @Test
-  public void read_nothing() {
-    SymbolsLineReader symbolsLineReader = newReader();
-
-    symbolsLineReader.read(line1);
-
-    assertThat(line1.getSymbols()).isEmpty();
-  }
-
-  @Test
-  public void read_symbols() {
-    SymbolsLineReader symbolsLineReader = newReader(newSymbol(
-      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_4, RANGE_LABEL_1),
-      newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2)
-      ));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-    symbolsLineReader.read(line3);
-
-    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
-    assertThat(line2.getSymbols()).isEmpty();
-    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
-  }
-
-  @Test
-  public void read_symbols_with_reference_on_same_line() {
-    SymbolsLineReader symbolsLineReader = newReader(newSymbol(
-      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_1),
-      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_2)
-      ));
-
-    symbolsLineReader.read(line1);
-
-    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_2 + ",1");
-  }
-
-  @Test
-  public void read_symbols_with_two_references() {
-    SymbolsLineReader symbolsLineReader = newReader(newSymbol(
-      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_4, RANGE_LABEL_1),
-      newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2),
-      newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_2, RANGE_LABEL_3)
-      ));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-    symbolsLineReader.read(line3);
-
-    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
-    assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_3 + ",1");
-    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
-  }
-
-  @Test
-  public void read_symbols_with_two_references_on_the_same_line() {
-    SymbolsLineReader symbolsLineReader = newReader(newSymbol(
-      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_1),
-      newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_1, RANGE_LABEL_2),
-      newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_2, OFFSET_3, RANGE_LABEL_3)
-      ));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-
-    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
-    assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_3 + ",1");
-  }
-
-  @Test
-  public void read_symbols_when_reference_line_is_before_declaration_line() {
-    SymbolsLineReader symbolsLineReader = newReader(newSymbol(
-      newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_3, OFFSET_4, RANGE_LABEL_1),
-      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_2, RANGE_LABEL_2)
-      ));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-
-    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
-    assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
-  }
-
-  @Test
-  public void read_many_symbols_on_lines() {
-    SymbolsLineReader symbolsLineReader = newReader(
-      newSymbol(
-        newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_2, RANGE_LABEL_1),
-        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_2)),
-      newSymbol(
-        newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_3, OFFSET_4, RANGE_LABEL_3),
-        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_4)
-      ));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-    symbolsLineReader.read(line3);
-
-    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_3 + ",2");
-    assertThat(line2.getSymbols()).isEmpty();
-    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_4 + ",2");
-  }
-
-  @Test
-  public void symbol_declaration_should_be_sorted_by_offset() {
-    SymbolsLineReader symbolsLineReader = newReader(
-      newSymbol(
-        // This symbol begins after the second symbol, it should appear in second place
-        newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_1),
-        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_1)),
-      newSymbol(
-        newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_2),
-        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_2)
-      ));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-    symbolsLineReader.read(line3);
-
-    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_1 + ",2");
-    assertThat(line2.getSymbols()).isEmpty();
-    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_1 + ",2");
-  }
-
-  @Test
-  public void symbol_declaration_should_be_sorted_by_line() {
-    SymbolsLineReader symbolsLineReader = newReader(
-      newSymbol(
-        newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_1, RANGE_LABEL_1),
-        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_2)),
-      newSymbol(
-        newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_1),
-        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_1)
-      ));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-    symbolsLineReader.read(line3);
-
-    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
-    assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",2");
-    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_2 + ",2");
-  }
-
-  @Test
-  public void read_symbols_defined_on_many_lines() {
-    TextRange declaration = newTextRange(LINE_1, LINE_2, OFFSET_1, OFFSET_3);
-    when(rangeOffsetConverter.offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
-    when(rangeOffsetConverter.offsetToString(declaration, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
-
-    TextRange reference = newTextRange(LINE_3, LINE_4, OFFSET_1, OFFSET_3);
-    when(rangeOffsetConverter.offsetToString(reference, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
-    when(rangeOffsetConverter.offsetToString(reference, LINE_4, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
-
-    SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-    symbolsLineReader.read(line3);
-    symbolsLineReader.read(line4);
-
-    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
-    assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
-    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
-    assertThat(line4.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
-  }
-
-  @Test
-  public void read_symbols_declared_on_a_whole_line() {
-    TextRange declaration = newTextRange(LINE_1, LINE_2, OFFSET_0, OFFSET_0);
-    when(rangeOffsetConverter.offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
-    when(rangeOffsetConverter.offsetToString(declaration, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn("");
-    TextRange reference = newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2);
-
-    SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-    symbolsLineReader.read(line3);
-    symbolsLineReader.read(line4);
-
-    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
-    assertThat(line2.getSymbols()).isEmpty();
-    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
-    assertThat(line4.getSymbols()).isEmpty();
-  }
-
-  @Test
-  public void not_fail_and_stop_processing_when_range_offset_converter_throw_RangeOffsetConverterException() {
-    TextRange declaration = newTextRange(LINE_1, LINE_1, OFFSET_1, OFFSET_3);
-    doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH);
-
-    TextRange reference = newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_1, OFFSET_3, RANGE_LABEL_2);
-
-    SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-
-    assertNoSymbol();
-    assertThat(logTester.logs(WARN)).isNotEmpty();
-  }
-
-  @Test
-  public void keep_existing_processed_symbols_when_range_offset_converter_throw_RangeOffsetConverterException() {
-    TextRange declaration = newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_3, RANGE_LABEL_2);
-
-    TextRange reference = newTextRange(LINE_2, LINE_2, OFFSET_1, OFFSET_3);
-    doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(reference, LINE_2, DEFAULT_LINE_LENGTH);
-
-    SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
-
-    symbolsLineReader.read(line1);
-    symbolsLineReader.read(line2);
-
-    assertThat(line1.hasSymbols()).isTrue();
-    assertThat(line2.hasSymbols()).isFalse();
-    assertThat(logTester.logs(WARN)).isNotEmpty();
-  }
-
-  @Test
-  public void display_file_key_in_warning_when_range_offset_converter_throw_RangeOffsetConverterException() {
-    TextRange declaration = newTextRange(LINE_1, LINE_1, OFFSET_1, OFFSET_3);
-    doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH);
-    SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_1, OFFSET_3, RANGE_LABEL_2)));
-
-    symbolsLineReader.read(line1);
-
-    assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Symbols data. Symbols will be ignored for file 'FILE_KEY'");
-  }
-
-  private ScannerReport.Symbol newSymbol(TextRange declaration, TextRange... references) {
-    ScannerReport.Symbol.Builder builder = ScannerReport.Symbol.newBuilder()
-      .setDeclaration(declaration);
-    for (TextRange reference : references) {
-      builder.addReference(reference);
-    }
-    return builder.build();
-  }
-
-  private SymbolsLineReader newReader(ScannerReport.Symbol... symbols) {
-    return new SymbolsLineReader(FILE, Arrays.asList(symbols).iterator(), rangeOffsetConverter);
-  }
-
-  private TextRange newSingleLineTextRangeWithExpectedLabel(int line, int startOffset, int endOffset, String rangeLabel) {
-    TextRange textRange = newTextRange(line, line, startOffset, endOffset);
-    when(rangeOffsetConverter.offsetToString(textRange, line, DEFAULT_LINE_LENGTH)).thenReturn(rangeLabel);
-    return textRange;
-  }
-
-  private static TextRange newTextRange(int startLine, int endLine, int startOffset, int endOffset) {
-    return TextRange.newBuilder()
-      .setStartLine(startLine).setEndLine(endLine)
-      .setStartOffset(startOffset).setEndOffset(endOffset)
-      .build();
-  }
-
-  private void assertNoSymbol() {
-    assertThat(line1.hasSymbols()).isFalse();
-    assertThat(line2.hasSymbols()).isFalse();
-    assertThat(line3.hasSymbols()).isFalse();
-    assertThat(line4.hasSymbols()).isFalse();
-  }
-
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReaderTest.java
new file mode 100644 (file)
index 0000000..54b9f15
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import java.util.Collections;
+import org.junit.Test;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.CoverageLineReader;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CoverageLineReaderTest {
+
+  @Test
+  public void set_coverage() {
+    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder()
+      .setLine(1)
+      .setConditions(10)
+      .setHits(true)
+      .setCoveredConditions(2)
+      .build()).iterator());
+
+    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
+    computeCoverageLine.read(lineBuilder);
+
+    assertThat(lineBuilder.getLineHits()).isEqualTo(1);
+    assertThat(lineBuilder.getConditions()).isEqualTo(10);
+    assertThat(lineBuilder.getCoveredConditions()).isEqualTo(2);
+  }
+
+  // Some tools are only able to report condition coverage
+  @Test
+  public void set_coverage_only_conditions() {
+    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder()
+      .setLine(1)
+      .setConditions(10)
+      .setCoveredConditions(2)
+      .build()).iterator());
+
+    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
+    computeCoverageLine.read(lineBuilder);
+
+    assertThat(lineBuilder.hasLineHits()).isFalse();
+    assertThat(lineBuilder.getConditions()).isEqualTo(10);
+  }
+
+  @Test
+  public void set_coverage_on_uncovered_lines() {
+    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder()
+      .setLine(1)
+      .setHits(false)
+      .build()).iterator());
+
+    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
+    computeCoverageLine.read(lineBuilder);
+
+    assertThat(lineBuilder.hasLineHits()).isTrue();
+    assertThat(lineBuilder.getLineHits()).isEqualTo(0);
+  }
+
+  @Test
+  public void nothing_to_do_when_no_coverage_info() {
+    CoverageLineReader computeCoverageLine = new CoverageLineReader(Collections.<ScannerReport.LineCoverage>emptyList().iterator());
+
+    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
+    computeCoverageLine.read(lineBuilder);
+
+    assertThat(lineBuilder.hasLineHits()).isFalse();
+    assertThat(lineBuilder.hasConditions()).isFalse();
+    assertThat(lineBuilder.hasCoveredConditions()).isFalse();
+  }
+
+  @Test
+  public void nothing_to_do_when_no_coverage_info_for_current_line() {
+    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(
+      ScannerReport.LineCoverage.newBuilder()
+        .setLine(1)
+        .setConditions(10)
+        .setHits(true)
+        .setCoveredConditions(2)
+        .build()
+    // No coverage info on line 2
+    ).iterator());
+
+    DbFileSources.Line.Builder line2Builder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(2);
+    computeCoverageLine.read(line2Builder);
+
+    assertThat(line2Builder.hasLineHits()).isFalse();
+    assertThat(line2Builder.hasConditions()).isFalse();
+    assertThat(line2Builder.hasCoveredConditions()).isFalse();
+  }
+
+  @Test
+  public void nothing_to_do_when_no_coverage_info_for_next_line() {
+    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(
+      ScannerReport.LineCoverage.newBuilder()
+        .setLine(1)
+        .setConditions(10)
+        .setHits(true)
+        .setCoveredConditions(2)
+        .build()
+    // No coverage info on line 2
+    ).iterator());
+
+    DbFileSources.Data.Builder fileSourceBuilder = DbFileSources.Data.newBuilder();
+    DbFileSources.Line.Builder line1Builder = fileSourceBuilder.addLinesBuilder().setLine(1);
+    DbFileSources.Line.Builder line2Builder = fileSourceBuilder.addLinesBuilder().setLine(2);
+    computeCoverageLine.read(line1Builder);
+    computeCoverageLine.read(line2Builder);
+
+    assertThat(line2Builder.hasLineHits()).isFalse();
+    assertThat(line2Builder.hasConditions()).isFalse();
+    assertThat(line2Builder.hasCoveredConditions()).isFalse();
+  }
+
+  @Test
+  public void does_not_set_deprecated_coverage_fields() {
+    CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder()
+      .setLine(1)
+      .setConditions(10)
+      .setHits(true)
+      .setCoveredConditions(2)
+      .build()).iterator());
+
+    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
+    computeCoverageLine.read(lineBuilder);
+
+    assertThat(lineBuilder.hasDeprecatedUtLineHits()).isFalse();
+    assertThat(lineBuilder.hasDeprecatedUtConditions()).isFalse();
+    assertThat(lineBuilder.hasDeprecatedUtCoveredConditions()).isFalse();
+    assertThat(lineBuilder.hasDeprecatedOverallLineHits()).isFalse();
+    assertThat(lineBuilder.hasDeprecatedOverallConditions()).isFalse();
+    assertThat(lineBuilder.hasDeprecatedOverallCoveredConditions()).isFalse();
+    assertThat(lineBuilder.hasDeprecatedItLineHits()).isFalse();
+    assertThat(lineBuilder.hasDeprecatedItConditions()).isFalse();
+    assertThat(lineBuilder.hasDeprecatedItCoveredConditions()).isFalse();
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReaderTest.java
new file mode 100644 (file)
index 0000000..8fb5c00
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Test;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
+import org.sonar.server.computation.task.projectanalysis.duplication.CrossProjectDuplicate;
+import org.sonar.server.computation.task.projectanalysis.duplication.Duplicate;
+import org.sonar.server.computation.task.projectanalysis.duplication.Duplication;
+import org.sonar.server.computation.task.projectanalysis.duplication.InProjectDuplicate;
+import org.sonar.server.computation.task.projectanalysis.duplication.InnerDuplicate;
+import org.sonar.server.computation.task.projectanalysis.duplication.TextBlock;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.DuplicationLineReader;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DuplicationLineReaderTest {
+
+  DbFileSources.Data.Builder sourceData = DbFileSources.Data.newBuilder();
+  DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1);
+  DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2);
+  DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3);
+  DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4);
+
+  @Test
+  public void read_nothing() {
+    DuplicationLineReader reader = new DuplicationLineReader(Collections.emptySet());
+
+    reader.read(line1);
+
+    assertThat(line1.getDuplicationList()).isEmpty();
+  }
+
+  @Test
+  public void read_duplication_with_duplicates_on_same_file() {
+    DuplicationLineReader reader = duplicationLineReader(duplication(1, 2, innerDuplicate(3, 4)));
+
+    reader.read(line1);
+    reader.read(line2);
+    reader.read(line3);
+    reader.read(line4);
+
+    assertThat(line1.getDuplicationList()).containsExactly(1);
+    assertThat(line2.getDuplicationList()).containsExactly(1);
+    assertThat(line3.getDuplicationList()).containsExactly(2);
+    assertThat(line4.getDuplicationList()).containsExactly(2);
+  }
+
+  @Test
+  public void read_duplication_with_duplicates_on_other_file() {
+    DuplicationLineReader reader = duplicationLineReader(
+        duplication(
+            1, 2,
+            new InProjectDuplicate(fileComponent(1).build(), new TextBlock(3, 4))));
+
+    reader.read(line1);
+    reader.read(line2);
+    reader.read(line3);
+    reader.read(line4);
+
+    assertThat(line1.getDuplicationList()).containsExactly(1);
+    assertThat(line2.getDuplicationList()).containsExactly(1);
+    assertThat(line3.getDuplicationList()).isEmpty();
+    assertThat(line4.getDuplicationList()).isEmpty();
+  }
+
+  @Test
+  public void read_duplication_with_duplicates_on_other_file_from_other_project() {
+    DuplicationLineReader reader = duplicationLineReader(
+        duplication(
+            1, 2,
+            new CrossProjectDuplicate("other-component-key-from-another-project", new TextBlock(3, 4))));
+
+    reader.read(line1);
+    reader.read(line2);
+    reader.read(line3);
+    reader.read(line4);
+
+    assertThat(line1.getDuplicationList()).containsExactly(1);
+    assertThat(line2.getDuplicationList()).containsExactly(1);
+    assertThat(line3.getDuplicationList()).isEmpty();
+    assertThat(line4.getDuplicationList()).isEmpty();
+  }
+
+  @Test
+  public void read_many_duplications() {
+    DuplicationLineReader reader = duplicationLineReader(
+        duplication(
+            1, 1,
+            innerDuplicate(2, 2)),
+        duplication(
+            1, 2,
+            innerDuplicate(3, 4))
+    );
+
+    reader.read(line1);
+    reader.read(line2);
+    reader.read(line3);
+    reader.read(line4);
+
+    assertThat(line1.getDuplicationList()).containsExactly(1, 2);
+    assertThat(line2.getDuplicationList()).containsExactly(2, 3);
+    assertThat(line3.getDuplicationList()).containsExactly(4);
+    assertThat(line4.getDuplicationList()).containsExactly(4);
+  }
+
+  @Test
+  public void should_be_sorted_by_line_block() {
+    DuplicationLineReader reader = duplicationLineReader(
+        duplication(
+            2, 2,
+            innerDuplicate(4, 4)),
+        duplication(
+            1, 1,
+            innerDuplicate(3, 3))
+    );
+
+    reader.read(line1);
+    reader.read(line2);
+    reader.read(line3);
+    reader.read(line4);
+
+    assertThat(line1.getDuplicationList()).containsExactly(1);
+    assertThat(line2.getDuplicationList()).containsExactly(2);
+    assertThat(line3.getDuplicationList()).containsExactly(3);
+    assertThat(line4.getDuplicationList()).containsExactly(4);
+  }
+
+  @Test
+  public void should_be_sorted_by_line_length() {
+    DuplicationLineReader reader = duplicationLineReader(
+        duplication(
+            1, 2,
+            innerDuplicate(3, 4)),
+        duplication(
+            1, 1,
+            innerDuplicate(4, 4)
+        )
+    );
+
+    reader.read(line1);
+    reader.read(line2);
+    reader.read(line3);
+    reader.read(line4);
+
+    assertThat(line1.getDuplicationList()).containsExactly(1, 2);
+    assertThat(line2.getDuplicationList()).containsExactly(2);
+    assertThat(line3.getDuplicationList()).containsExactly(3);
+    assertThat(line4.getDuplicationList()).containsExactly(3, 4);
+  }
+
+  private static ReportComponent.Builder fileComponent(int ref) {
+    return ReportComponent.builder(Component.Type.FILE, ref);
+  }
+
+  private static DuplicationLineReader duplicationLineReader(Duplication... duplications) {
+    return new DuplicationLineReader(ImmutableSet.copyOf(Arrays.asList(duplications)));
+  }
+
+  private static Duplication duplication(int originalStart, int originalEnd, Duplicate... duplicates) {
+    return new Duplication(new TextBlock(originalStart, originalEnd), Arrays.asList(duplicates));
+  }
+
+  private static InnerDuplicate innerDuplicate(int start, int end) {
+    return new InnerDuplicate(new TextBlock(start, end));
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReaderTest.java
new file mode 100644 (file)
index 0000000..99f9778
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType;
+import org.sonar.scanner.protocol.output.ScannerReport.TextRange;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.HighlightingLineReader;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.RangeOffsetConverterException;
+
+import static com.google.common.collect.ImmutableMap.of;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.utils.log.LoggerLevel.WARN;
+import static org.sonar.db.protobuf.DbFileSources.Data.newBuilder;
+import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.ANNOTATION;
+import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.COMMENT;
+import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.CONSTANT;
+import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.CPP_DOC;
+import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.HIGHLIGHTING_STRING;
+import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.KEYWORD;
+import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
+
+public class HighlightingLineReaderTest {
+
+  @Rule
+  public LogTester logTester = new LogTester();
+
+  private static final Component FILE = builder(Component.Type.FILE, 1).setUuid("FILE_UUID").setKey("FILE_KEY").build();
+
+  private static final int DEFAULT_LINE_LENGTH = 5;
+
+  private static final int LINE_1 = 1;
+  private static final int LINE_2 = 2;
+  private static final int LINE_3 = 3;
+  private static final int LINE_4 = 4;
+
+  private static final String RANGE_LABEL_1 = "1,2";
+  private static final String RANGE_LABEL_2 = "2,3";
+  private static final String RANGE_LABEL_3 = "3,4";
+  private static final String RANGE_LABEL_4 = "0,2";
+  private static final String RANGE_LABEL_5 = "0,3";
+
+  private RangeOffsetConverter rangeOffsetConverter = mock(RangeOffsetConverter.class);
+
+  private DbFileSources.Data.Builder sourceData = newBuilder();
+  private DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1);
+  private DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2);
+  private DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3);
+  private DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4);
+
+  @Test
+  public void nothing_to_read() {
+    HighlightingLineReader highlightingLineReader = newReader(Collections.emptyMap());
+
+    DbFileSources.Line.Builder lineBuilder = newBuilder().addLinesBuilder().setLine(1);
+    highlightingLineReader.read(lineBuilder);
+
+    assertThat(lineBuilder.hasHighlighting()).isFalse();
+  }
+
+  @Test
+  public void read_one_line() {
+    HighlightingLineReader highlightingLineReader = newReader(of(
+      newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION));
+
+    highlightingLineReader.read(line1);
+
+    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
+  }
+
+  @Test
+  public void read_many_lines() {
+    HighlightingLineReader highlightingLineReader = newReader(of(
+      newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
+      newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_2), COMMENT,
+      newSingleLineTextRangeWithExpectingLabel(LINE_4, RANGE_LABEL_3), CONSTANT));
+
+    highlightingLineReader.read(line1);
+    highlightingLineReader.read(line2);
+    highlightingLineReader.read(line3);
+    highlightingLineReader.read(line4);
+
+    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
+    assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",cd");
+    assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",c");
+  }
+
+  @Test
+  public void supports_highlighting_over_multiple_lines_including_an_empty_one() {
+    List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList = new ArrayList<>();
+    addHighlighting(syntaxHighlightingList, 1, 0, 1, 7, KEYWORD); // package
+    addHighlighting(syntaxHighlightingList, 2, 0, 4, 6, CPP_DOC); // comment over 3 lines
+    addHighlighting(syntaxHighlightingList, 5, 0, 5, 6, KEYWORD); // public
+    addHighlighting(syntaxHighlightingList, 5, 7, 5, 12, KEYWORD); // class
+    HighlightingLineReader highlightingLineReader = new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), new RangeOffsetConverter());
+
+    DbFileSources.Line.Builder[] builders = new DbFileSources.Line.Builder[] {
+      addSourceLine(highlightingLineReader, 1, "package example;"),
+      addSourceLine(highlightingLineReader, 2, "/*"),
+      addSourceLine(highlightingLineReader, 3, ""),
+      addSourceLine(highlightingLineReader, 4, " foo*/"),
+      addSourceLine(highlightingLineReader, 5, "public class One {"),
+      addSourceLine(highlightingLineReader, 6, "}")
+    };
+
+    assertThat(builders)
+      .extracting("highlighting")
+      .containsExactly(
+        "0,7,k",
+        "0,2,cppd",
+        "",
+        "0,6,cppd",
+        "0,6,k;7,12,k",
+        "");
+  }
+
+  private DbFileSources.Line.Builder addSourceLine(HighlightingLineReader highlightingLineReader, int line, String source) {
+    DbFileSources.Line.Builder lineBuilder = sourceData.addLinesBuilder().setSource(source).setLine(line);
+    highlightingLineReader.read(lineBuilder);
+    return lineBuilder;
+  }
+
+  private void addHighlighting(List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList,
+    int startLine, int startOffset,
+    int endLine, int endOffset,
+    HighlightingType type) {
+    TextRange.Builder textRangeBuilder = TextRange.newBuilder();
+    ScannerReport.SyntaxHighlightingRule.Builder ruleBuilder = ScannerReport.SyntaxHighlightingRule.newBuilder();
+    syntaxHighlightingList.add(ruleBuilder
+      .setRange(textRangeBuilder
+        .setStartLine(startLine).setEndLine(endLine)
+        .setStartOffset(startOffset).setEndOffset(endOffset)
+        .build())
+      .setType(type)
+      .build());
+  }
+
+  @Test
+  public void read_many_syntax_highlighting_on_same_line() {
+    HighlightingLineReader highlightingLineReader = newReader(of(
+      newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
+      newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_2), COMMENT));
+
+    highlightingLineReader.read(line1);
+
+    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a;" + RANGE_LABEL_2 + ",cd");
+  }
+
+  @Test
+  public void read_one_syntax_highlighting_on_many_lines() {
+    // This highlighting begin on line 1 and finish on line 3
+    TextRange textRange = newTextRange(LINE_1, LINE_3);
+    when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
+    when(rangeOffsetConverter.offsetToString(textRange, LINE_2, 6)).thenReturn(RANGE_LABEL_2);
+    when(rangeOffsetConverter.offsetToString(textRange, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3);
+
+    HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION));
+
+    highlightingLineReader.read(line1);
+    DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line 2").setLine(2);
+    highlightingLineReader.read(line2);
+    highlightingLineReader.read(line3);
+
+    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
+    assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a");
+    assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a");
+  }
+
+  @Test
+  public void read_many_syntax_highlighting_on_many_lines() {
+    TextRange textRange1 = newTextRange(LINE_1, LINE_3);
+    when(rangeOffsetConverter.offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
+    when(rangeOffsetConverter.offsetToString(textRange1, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
+    when(rangeOffsetConverter.offsetToString(textRange1, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3);
+
+    TextRange textRange2 = newTextRange(LINE_2, LINE_4);
+    when(rangeOffsetConverter.offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
+    when(rangeOffsetConverter.offsetToString(textRange2, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
+    when(rangeOffsetConverter.offsetToString(textRange2, LINE_4, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_4);
+
+    TextRange textRange3 = newTextRange(LINE_2, LINE_2);
+    when(rangeOffsetConverter.offsetToString(textRange3, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_5);
+
+    HighlightingLineReader highlightingLineReader = newReader(of(
+      textRange1, ANNOTATION,
+      textRange2, HIGHLIGHTING_STRING,
+      textRange3, COMMENT));
+
+    highlightingLineReader.read(line1);
+    highlightingLineReader.read(line2);
+    highlightingLineReader.read(line3);
+    highlightingLineReader.read(line4);
+
+    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
+    assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a;" + RANGE_LABEL_2 + ",s;" + RANGE_LABEL_5 + ",cd");
+    assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a;" + RANGE_LABEL_2 + ",s");
+    assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_4 + ",s");
+  }
+
+  @Test
+  public void read_highlighting_declared_on_a_whole_line() {
+    TextRange textRange = newTextRange(LINE_1, LINE_2);
+    when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
+    when(rangeOffsetConverter.offsetToString(textRange, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn("");
+
+    HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION));
+
+    highlightingLineReader.read(line1);
+    highlightingLineReader.read(line2);
+    highlightingLineReader.read(line3);
+
+    assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
+    // Nothing should be set on line 2
+    assertThat(line2.getHighlighting()).isEmpty();
+    assertThat(line3.getHighlighting()).isEmpty();
+  }
+
+  @Test
+  public void not_fail_and_stop_processing_when_range_offset_converter_throw_RangeOffsetConverterException() {
+    TextRange textRange1 = newTextRange(LINE_1, LINE_1);
+    doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH);
+
+    HighlightingLineReader highlightingLineReader = newReader(of(
+      textRange1, HighlightingType.ANNOTATION,
+      newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_1), HIGHLIGHTING_STRING));
+
+    highlightingLineReader.read(line1);
+    highlightingLineReader.read(line2);
+
+    assertNoHighlighting();
+    assertThat(logTester.logs(WARN)).isNotEmpty();
+  }
+
+  @Test
+  public void keep_existing_processed_highlighting_when_range_offset_converter_throw_RangeOffsetConverterException() {
+    TextRange textRange2 = newTextRange(LINE_2, LINE_2);
+    doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH);
+
+    HighlightingLineReader highlightingLineReader = newReader(of(
+      newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
+      textRange2, HIGHLIGHTING_STRING));
+
+    highlightingLineReader.read(line1);
+    highlightingLineReader.read(line2);
+
+    assertThat(line1.hasHighlighting()).isTrue();
+    assertThat(line2.hasHighlighting()).isFalse();
+    assertThat(logTester.logs(WARN)).isNotEmpty();
+  }
+
+  @Test
+  public void display_file_key_in_warning_when_range_offset_converter_throw_RangeOffsetConverterException() {
+    TextRange textRange1 = newTextRange(LINE_1, LINE_1);
+    doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH);
+    HighlightingLineReader highlightingLineReader = newReader(of(textRange1, ANNOTATION));
+
+    highlightingLineReader.read(line1);
+
+    assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Highlighting data. Highlighting will be ignored for file 'FILE_KEY'");
+  }
+
+  private HighlightingLineReader newReader(Map<TextRange, HighlightingType> textRangeByType) {
+    List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList = new ArrayList<>();
+    for (Map.Entry<TextRange, HighlightingType> entry : textRangeByType.entrySet()) {
+      syntaxHighlightingList.add(ScannerReport.SyntaxHighlightingRule.newBuilder()
+        .setRange(entry.getKey())
+        .setType(entry.getValue())
+        .build());
+    }
+    return new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), rangeOffsetConverter);
+  }
+
+  private static TextRange newTextRange(int startLine, int enLine) {
+    Random random = new Random();
+    return TextRange.newBuilder()
+      .setStartLine(startLine).setEndLine(enLine)
+      // Offsets are not used by the reader
+      .setStartOffset(random.nextInt()).setEndOffset(random.nextInt())
+      .build();
+  }
+
+  private TextRange newSingleLineTextRangeWithExpectingLabel(int line, String rangeLabel) {
+    TextRange textRange = newTextRange(line, line);
+    when(rangeOffsetConverter.offsetToString(textRange, line, DEFAULT_LINE_LENGTH)).thenReturn(rangeLabel);
+    return textRange;
+  }
+
+  private void assertNoHighlighting() {
+    assertThat(line1.hasHighlighting()).isFalse();
+    assertThat(line2.hasHighlighting()).isFalse();
+    assertThat(line3.hasHighlighting()).isFalse();
+    assertThat(line4.hasHighlighting()).isFalse();
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReaderTest.java
new file mode 100644 (file)
index 0000000..ce3eea1
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.Map;
+import org.junit.Test;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.server.computation.task.projectanalysis.scm.Changeset;
+import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo;
+import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoImpl;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.ScmLineReader;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ScmLineReaderTest {
+
+  @Test
+  public void set_scm() {
+    ScmInfo scmInfo = new ScmInfoImpl(Collections.singletonMap(1,
+      Changeset.newChangesetBuilder()
+        .setAuthor("john")
+        .setDate(123_456_789L)
+        .setRevision("rev-1")
+        .build()));
+
+    ScmLineReader lineScm = new ScmLineReader(scmInfo);
+
+    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
+    lineScm.read(lineBuilder);
+
+    assertThat(lineBuilder.getScmAuthor()).isEqualTo("john");
+    assertThat(lineBuilder.getScmDate()).isEqualTo(123_456_789L);
+    assertThat(lineBuilder.getScmRevision()).isEqualTo("rev-1");
+  }
+
+  @Test
+  public void set_scm_with_minim_fields() {
+    ScmInfo scmInfo = new ScmInfoImpl(Collections.singletonMap(1,
+      Changeset.newChangesetBuilder()
+        .setDate(123456789L)
+        .build()));
+
+    ScmLineReader lineScm = new ScmLineReader(scmInfo);
+
+    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
+    lineScm.read(lineBuilder);
+
+    assertThat(lineBuilder.hasScmAuthor()).isFalse();
+    assertThat(lineBuilder.getScmDate()).isEqualTo(123456789L);
+    assertThat(lineBuilder.hasScmRevision()).isFalse();
+
+  }
+
+  @Test
+  public void getLatestChange_returns_changeset_with_highest_date_of_read_lines() {
+    long refDate = 123_456_789L;
+    Changeset changeset0 = Changeset.newChangesetBuilder().setDate(refDate - 636).setRevision("rev-1").build();
+    Changeset changeset1 = Changeset.newChangesetBuilder().setDate(refDate + 1).setRevision("rev-2").build();
+    Changeset changeset2 = Changeset.newChangesetBuilder().setDate(refDate + 2).build();
+    ScmInfo scmInfo = new ScmInfoImpl(setup8LinesChangeset(changeset0, changeset1, changeset2));
+
+    ScmLineReader lineScm = new ScmLineReader(scmInfo);
+
+    // before any line is read, the latest changes are null
+    assertThat(lineScm.getLatestChange()).isNull();
+    assertThat(lineScm.getLatestChangeWithRevision()).isNull();
+
+    // read line 1, only one changeset => 0
+    readLineAndAssertLatestChanges(lineScm, 1, changeset0, changeset0);
+
+    // read line 2, latest changeset is 1
+    readLineAndAssertLatestChanges(lineScm, 2, changeset1, changeset1);
+
+    // read line 3, latest changeset is still 1
+    readLineAndAssertLatestChanges(lineScm, 3, changeset1, changeset1);
+
+    // read line 4, latest changeset is now 2
+    readLineAndAssertLatestChanges(lineScm, 4, changeset2, changeset1);
+
+    // read line 5 to 8, there will never be any changeset more recent than 2
+    readLineAndAssertLatestChanges(lineScm, 5, changeset2, changeset1);
+    readLineAndAssertLatestChanges(lineScm, 6, changeset2, changeset1);
+    readLineAndAssertLatestChanges(lineScm, 7, changeset2, changeset1);
+    readLineAndAssertLatestChanges(lineScm, 8, changeset2, changeset1);
+  }
+
+  private static Map<Integer, Changeset> setup8LinesChangeset(Changeset changeset0, Changeset changeset1, Changeset changeset2) {
+    return ImmutableMap.<Integer, Changeset>builder()
+      .put(1, changeset0)
+      .put(2, changeset1)
+      .put(3, changeset1)
+      .put(4, changeset2)
+      .put(5, changeset0)
+      .put(6, changeset1)
+      .put(7, changeset0)
+      .put(8, changeset0).build();
+  }
+
+  private void readLineAndAssertLatestChanges(ScmLineReader lineScm, int line, Changeset expectedChangeset, Changeset expectedChangesetWithRevision) {
+    DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(line);
+    lineScm.read(lineBuilder);
+    assertThat(lineScm.getLatestChange()).isSameAs(expectedChangeset);
+    assertThat(lineScm.getLatestChangeWithRevision()).isSameAs(expectedChangesetWithRevision);
+
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReaderTest.java
new file mode 100644 (file)
index 0000000..ade3730
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.task.projectanalysis.source.linereader;
+
+import java.util.Arrays;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.TextRange;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter;
+import org.sonar.server.computation.task.projectanalysis.source.linereader.SymbolsLineReader;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.utils.log.LoggerLevel.WARN;
+import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
+
+public class SymbolsLineReaderTest {
+
+  @Rule
+  public LogTester logTester = new LogTester();
+
+  private static final Component FILE = builder(Component.Type.FILE, 1).setUuid("FILE_UUID").setKey("FILE_KEY").build();
+
+  private static final int DEFAULT_LINE_LENGTH = 5;
+
+  private static final int LINE_1 = 1;
+  private static final int LINE_2 = 2;
+  private static final int LINE_3 = 3;
+  private static final int LINE_4 = 4;
+
+  private static final int OFFSET_0 = 0;
+  private static final int OFFSET_1 = 1;
+  private static final int OFFSET_2 = 2;
+  private static final int OFFSET_3 = 3;
+  private static final int OFFSET_4 = 4;
+
+  private static final String RANGE_LABEL_1 = "1,2";
+  private static final String RANGE_LABEL_2 = "2,3";
+  private static final String RANGE_LABEL_3 = "3,4";
+  private static final String RANGE_LABEL_4 = "0,2";
+
+  private RangeOffsetConverter rangeOffsetConverter = mock(RangeOffsetConverter.class);
+
+  private DbFileSources.Data.Builder sourceData = DbFileSources.Data.newBuilder();
+  private DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1);
+  private DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2);
+  private DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3);
+  private DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4);
+
+  @Test
+  public void read_nothing() {
+    SymbolsLineReader symbolsLineReader = newReader();
+
+    symbolsLineReader.read(line1);
+
+    assertThat(line1.getSymbols()).isEmpty();
+  }
+
+  @Test
+  public void read_symbols() {
+    SymbolsLineReader symbolsLineReader = newReader(newSymbol(
+      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_4, RANGE_LABEL_1),
+      newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2)
+      ));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+    symbolsLineReader.read(line3);
+
+    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
+    assertThat(line2.getSymbols()).isEmpty();
+    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
+  }
+
+  @Test
+  public void read_symbols_with_reference_on_same_line() {
+    SymbolsLineReader symbolsLineReader = newReader(newSymbol(
+      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_1),
+      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_2)
+      ));
+
+    symbolsLineReader.read(line1);
+
+    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_2 + ",1");
+  }
+
+  @Test
+  public void read_symbols_with_two_references() {
+    SymbolsLineReader symbolsLineReader = newReader(newSymbol(
+      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_4, RANGE_LABEL_1),
+      newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2),
+      newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_2, RANGE_LABEL_3)
+      ));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+    symbolsLineReader.read(line3);
+
+    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
+    assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_3 + ",1");
+    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
+  }
+
+  @Test
+  public void read_symbols_with_two_references_on_the_same_line() {
+    SymbolsLineReader symbolsLineReader = newReader(newSymbol(
+      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_1),
+      newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_1, RANGE_LABEL_2),
+      newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_2, OFFSET_3, RANGE_LABEL_3)
+      ));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+
+    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
+    assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_3 + ",1");
+  }
+
+  @Test
+  public void read_symbols_when_reference_line_is_before_declaration_line() {
+    SymbolsLineReader symbolsLineReader = newReader(newSymbol(
+      newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_3, OFFSET_4, RANGE_LABEL_1),
+      newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_2, RANGE_LABEL_2)
+      ));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+
+    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
+    assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
+  }
+
+  @Test
+  public void read_many_symbols_on_lines() {
+    SymbolsLineReader symbolsLineReader = newReader(
+      newSymbol(
+        newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_2, RANGE_LABEL_1),
+        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_2)),
+      newSymbol(
+        newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_3, OFFSET_4, RANGE_LABEL_3),
+        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_4)
+      ));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+    symbolsLineReader.read(line3);
+
+    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_3 + ",2");
+    assertThat(line2.getSymbols()).isEmpty();
+    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_4 + ",2");
+  }
+
+  @Test
+  public void symbol_declaration_should_be_sorted_by_offset() {
+    SymbolsLineReader symbolsLineReader = newReader(
+      newSymbol(
+        // This symbol begins after the second symbol, it should appear in second place
+        newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_1),
+        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_1)),
+      newSymbol(
+        newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_2),
+        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_2)
+      ));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+    symbolsLineReader.read(line3);
+
+    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_1 + ",2");
+    assertThat(line2.getSymbols()).isEmpty();
+    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_1 + ",2");
+  }
+
+  @Test
+  public void symbol_declaration_should_be_sorted_by_line() {
+    SymbolsLineReader symbolsLineReader = newReader(
+      newSymbol(
+        newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_1, RANGE_LABEL_1),
+        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_2)),
+      newSymbol(
+        newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_1),
+        newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_1)
+      ));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+    symbolsLineReader.read(line3);
+
+    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
+    assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",2");
+    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_2 + ",2");
+  }
+
+  @Test
+  public void read_symbols_defined_on_many_lines() {
+    TextRange declaration = newTextRange(LINE_1, LINE_2, OFFSET_1, OFFSET_3);
+    when(rangeOffsetConverter.offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
+    when(rangeOffsetConverter.offsetToString(declaration, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
+
+    TextRange reference = newTextRange(LINE_3, LINE_4, OFFSET_1, OFFSET_3);
+    when(rangeOffsetConverter.offsetToString(reference, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
+    when(rangeOffsetConverter.offsetToString(reference, LINE_4, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
+
+    SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+    symbolsLineReader.read(line3);
+    symbolsLineReader.read(line4);
+
+    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
+    assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
+    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
+    assertThat(line4.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
+  }
+
+  @Test
+  public void read_symbols_declared_on_a_whole_line() {
+    TextRange declaration = newTextRange(LINE_1, LINE_2, OFFSET_0, OFFSET_0);
+    when(rangeOffsetConverter.offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
+    when(rangeOffsetConverter.offsetToString(declaration, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn("");
+    TextRange reference = newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2);
+
+    SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+    symbolsLineReader.read(line3);
+    symbolsLineReader.read(line4);
+
+    assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
+    assertThat(line2.getSymbols()).isEmpty();
+    assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
+    assertThat(line4.getSymbols()).isEmpty();
+  }
+
+  @Test
+  public void not_fail_and_stop_processing_when_range_offset_converter_throw_RangeOffsetConverterException() {
+    TextRange declaration = newTextRange(LINE_1, LINE_1, OFFSET_1, OFFSET_3);
+    doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH);
+
+    TextRange reference = newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_1, OFFSET_3, RANGE_LABEL_2);
+
+    SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+
+    assertNoSymbol();
+    assertThat(logTester.logs(WARN)).isNotEmpty();
+  }
+
+  @Test
+  public void keep_existing_processed_symbols_when_range_offset_converter_throw_RangeOffsetConverterException() {
+    TextRange declaration = newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_3, RANGE_LABEL_2);
+
+    TextRange reference = newTextRange(LINE_2, LINE_2, OFFSET_1, OFFSET_3);
+    doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(reference, LINE_2, DEFAULT_LINE_LENGTH);
+
+    SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
+
+    symbolsLineReader.read(line1);
+    symbolsLineReader.read(line2);
+
+    assertThat(line1.hasSymbols()).isTrue();
+    assertThat(line2.hasSymbols()).isFalse();
+    assertThat(logTester.logs(WARN)).isNotEmpty();
+  }
+
+  @Test
+  public void display_file_key_in_warning_when_range_offset_converter_throw_RangeOffsetConverterException() {
+    TextRange declaration = newTextRange(LINE_1, LINE_1, OFFSET_1, OFFSET_3);
+    doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH);
+    SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_1, OFFSET_3, RANGE_LABEL_2)));
+
+    symbolsLineReader.read(line1);
+
+    assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Symbols data. Symbols will be ignored for file 'FILE_KEY'");
+  }
+
+  private ScannerReport.Symbol newSymbol(TextRange declaration, TextRange... references) {
+    ScannerReport.Symbol.Builder builder = ScannerReport.Symbol.newBuilder()
+      .setDeclaration(declaration);
+    for (TextRange reference : references) {
+      builder.addReference(reference);
+    }
+    return builder.build();
+  }
+
+  private SymbolsLineReader newReader(ScannerReport.Symbol... symbols) {
+    return new SymbolsLineReader(FILE, Arrays.asList(symbols).iterator(), rangeOffsetConverter);
+  }
+
+  private TextRange newSingleLineTextRangeWithExpectedLabel(int line, int startOffset, int endOffset, String rangeLabel) {
+    TextRange textRange = newTextRange(line, line, startOffset, endOffset);
+    when(rangeOffsetConverter.offsetToString(textRange, line, DEFAULT_LINE_LENGTH)).thenReturn(rangeLabel);
+    return textRange;
+  }
+
+  private static TextRange newTextRange(int startLine, int endLine, int startOffset, int endOffset) {
+    return TextRange.newBuilder()
+      .setStartLine(startLine).setEndLine(endLine)
+      .setStartOffset(startOffset).setEndOffset(endOffset)
+      .build();
+  }
+
+  private void assertNoSymbol() {
+    assertThat(line1.hasSymbols()).isFalse();
+    assertThat(line2.hasSymbols()).isFalse();
+    assertThat(line3.hasSymbols()).isFalse();
+    assertThat(line4.hasSymbols()).isFalse();
+  }
+
+}
index 523ea9c03462815e44c87da6d58bee5728290a8f..921552352bb4bdc496c818c7058c83246027ad00 100644 (file)
@@ -104,7 +104,7 @@ public class PersistTestsStepTest extends BaseStepTest {
   public void no_test_in_database_and_batch_report() {
     underTest.execute();
 
-    assertThat(dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1)).isNull();
+    assertThat(dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1)).isNull();
     assertThat(log.logs()).isEmpty();
   }
 
@@ -121,7 +121,7 @@ public class PersistTestsStepTest extends BaseStepTest {
 
     assertThat(db.countRowsOfTable("file_sources")).isEqualTo(1);
 
-    FileSourceDto dto = dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1);
+    FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1);
     assertThat(dto.getCreatedAt()).isEqualTo(now);
     assertThat(dto.getUpdatedAt()).isEqualTo(now);
     assertThat(dto.getProjectUuid()).isEqualTo(PROJECT_UUID);
@@ -142,7 +142,7 @@ public class PersistTestsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    FileSourceDto dto = dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1);
+    FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1);
     assertThat(dto.getCreatedAt()).isEqualTo(now);
     assertThat(dto.getUpdatedAt()).isEqualTo(now);
     assertThat(dto.getProjectUuid()).isEqualTo(PROJECT_UUID);
@@ -168,7 +168,7 @@ public class PersistTestsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    FileSourceDto dto = dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1);
+    FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1);
     assertThat(dto.getFileUuid()).isEqualTo(TEST_FILE_UUID_1);
     List<DbFileSources.Test> tests = dto.getTestData();
     assertThat(tests).hasSize(1);
@@ -203,7 +203,7 @@ public class PersistTestsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    FileSourceDto dto = dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1);
+    FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1);
     List<Integer> coveredLines = dto.getTestData().get(0).getCoveredFile(0).getCoveredLineList();
     assertThat(coveredLines).containsOnly(1, 2, 3, 4);
   }
@@ -225,7 +225,7 @@ public class PersistTestsStepTest extends BaseStepTest {
       .setCreatedAt(100_000)
       .setUpdatedAt(100_000));
     db.getSession().commit();
-    assertThat(dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1)).isNotNull();
+    assertThat(dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1)).isNotNull();
 
     ScannerReport.Test newBatchTest = newTest(1);
     reportReader.putTests(TEST_FILE_REF_1, Arrays.asList(newBatchTest));
@@ -237,7 +237,7 @@ public class PersistTestsStepTest extends BaseStepTest {
     underTest.execute();
 
     // ASSERT
-    FileSourceDto dto = dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1);
+    FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1);
     assertThat(dto.getCreatedAt()).isEqualTo(100_000);
     assertThat(dto.getUpdatedAt()).isEqualTo(now);
     assertThat(dto.getTestData()).hasSize(1);
index b85906bd73336109470e3145f2ec466b3816e4e3..b64413f0687f9f03870c73bbcce6ebf0da2a36ee 100644 (file)
@@ -19,9 +19,9 @@
  */
 package org.sonar.core.hash;
 
-import com.google.common.collect.ImmutableList;
 import java.security.MessageDigest;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import org.apache.commons.codec.binary.Hex;
 import org.apache.commons.codec.digest.DigestUtils;
@@ -52,7 +52,7 @@ public class SourceLinesHashesComputer {
   }
 
   public List<String> getLineHashes() {
-    return ImmutableList.copyOf(lineHashes);
+    return Collections.unmodifiableList(lineHashes);
   }
 
   private String computeHash(String line) {
index 2fb837b2558de2161fb88bc008ac7b0e7a1443ec..d5e66955ab309b45b80a2c2c9cfa398f80331f58 100644 (file)
@@ -120,6 +120,19 @@ public class ScannerReportReader {
     return file.exists();
   }
 
+  public CloseableIterator<ScannerReport.LineSgnificantCode> readComponentSignificantCode(int fileRef) {
+    File file = fileStructure.fileFor(FileStructure.Domain.SGNIFICANT_CODE, fileRef);
+    if (fileExists(file)) {
+      return Protobuf.readStream(file, ScannerReport.LineSgnificantCode.parser());
+    }
+    return emptyCloseableIterator();
+  }
+
+  public boolean hasSignificantCode(int fileRef) {
+    File file = fileStructure.fileFor(FileStructure.Domain.SGNIFICANT_CODE, fileRef);
+    return fileExists(file);
+  }
+
   public CloseableIterator<ScannerReport.SyntaxHighlightingRule> readComponentSyntaxHighlighting(int fileRef) {
     File file = fileStructure.fileFor(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, fileRef);
     if (fileExists(file)) {