3 * Copyright (C) 2009-2019 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.ce.task.projectanalysis.source;
22 import com.google.common.base.Preconditions;
23 import java.util.HashMap;
24 import java.util.HashSet;
26 import java.util.Optional;
28 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
29 import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
30 import org.sonar.ce.task.projectanalysis.component.Component;
31 import org.sonar.ce.task.projectanalysis.period.Period;
32 import org.sonar.ce.task.projectanalysis.period.PeriodHolder;
33 import org.sonar.ce.task.projectanalysis.scm.Changeset;
34 import org.sonar.ce.task.projectanalysis.scm.ScmInfo;
35 import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepository;
37 public class NewLinesRepository {
38 private final BatchReportReader reportReader;
39 private final AnalysisMetadataHolder analysisMetadataHolder;
40 private final ScmInfoRepository scmInfoRepository;
41 private final PeriodHolder periodHolder;
42 private final Map<Component, Optional<Set<Integer>>> reportChangedLinesCache = new HashMap<>();
44 public NewLinesRepository(BatchReportReader reportReader, AnalysisMetadataHolder analysisMetadataHolder, PeriodHolder periodHolder, ScmInfoRepository scmInfoRepository) {
45 this.reportReader = reportReader;
46 this.analysisMetadataHolder = analysisMetadataHolder;
47 this.scmInfoRepository = scmInfoRepository;
48 this.periodHolder = periodHolder;
51 public boolean newLinesAvailable() {
52 return isPullRequestOrShortLivedBranch() || periodHolder.hasPeriod();
55 public Optional<Set<Integer>> getNewLines(Component file) {
56 Preconditions.checkArgument(file.getType() == Component.Type.FILE, "Changed lines are only available on files, but was: " + file.getType().name());
57 Optional<Set<Integer>> reportChangedLines = getChangedLinesFromReport(file);
58 if (reportChangedLines.isPresent()) {
59 return reportChangedLines;
61 return computeNewLinesFromScm(file);
65 * If the changed lines are not in the report or if we are not analyzing a short lived branch (or P/R) we fall back to this method.
66 * If there is a period and SCM information, we compare the change dates of each line with the start of the period to figure out
67 * if a line is new or not.
69 private Optional<Set<Integer>> computeNewLinesFromScm(Component component) {
70 if (!periodHolder.hasPeriod()) {
71 return Optional.empty();
74 Optional<ScmInfo> scmInfoOpt = scmInfoRepository.getScmInfo(component);
75 if (!scmInfoOpt.isPresent()) {
76 return Optional.empty();
79 ScmInfo scmInfo = scmInfoOpt.get();
80 Map<Integer, Changeset> allChangesets = scmInfo.getAllChangesets();
81 Set<Integer> lines = new HashSet<>();
83 for (Map.Entry<Integer, Changeset> e : allChangesets.entrySet()) {
84 if (isLineInPeriod(e.getValue().getDate(), periodHolder.getPeriod())) {
85 lines.add(e.getKey());
88 return Optional.of(lines);
92 * A line belongs to a Period if its date is older than the SNAPSHOT's date of the period.
94 private static boolean isLineInPeriod(long lineDate, Period period) {
95 return lineDate > period.getSnapshotDate();
98 private Optional<Set<Integer>> getChangedLinesFromReport(Component file) {
99 if (isPullRequestOrShortLivedBranch()) {
100 return reportChangedLinesCache.computeIfAbsent(file, this::readFromReport);
103 return Optional.empty();
106 private boolean isPullRequestOrShortLivedBranch() {
107 return analysisMetadataHolder.isPullRequest() || analysisMetadataHolder.isShortLivingBranch();
110 private Optional<Set<Integer>> readFromReport(Component file) {
111 return reportReader.readComponentChangedLines(file.getReportAttributes().getRef())
112 .map(c -> new HashSet<>(c.getLineList()));