]> source.dussan.org Git - sonarqube.git/blob
c59b4ee12a259698a11d48c307d11ca6338d31d1
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2019 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.ce.task.projectanalysis.source;
21
22 import com.google.common.base.Preconditions;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Map;
26 import java.util.Optional;
27 import java.util.Set;
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;
36
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<>();
43
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;
49   }
50
51   public boolean newLinesAvailable() {
52     return isPullRequestOrShortLivedBranch() || periodHolder.hasPeriod();
53   }
54
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;
60     }
61     return computeNewLinesFromScm(file);
62   }
63
64   /**
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.
68    */
69   private Optional<Set<Integer>> computeNewLinesFromScm(Component component) {
70     if (!periodHolder.hasPeriod()) {
71       return Optional.empty();
72     }
73
74     Optional<ScmInfo> scmInfoOpt = scmInfoRepository.getScmInfo(component);
75     if (!scmInfoOpt.isPresent()) {
76       return Optional.empty();
77     }
78
79     ScmInfo scmInfo = scmInfoOpt.get();
80     Map<Integer, Changeset> allChangesets = scmInfo.getAllChangesets();
81     Set<Integer> lines = new HashSet<>();
82
83     for (Map.Entry<Integer, Changeset> e : allChangesets.entrySet()) {
84       if (isLineInPeriod(e.getValue().getDate(), periodHolder.getPeriod())) {
85         lines.add(e.getKey());
86       }
87     }
88     return Optional.of(lines);
89   }
90
91   /**
92    * A line belongs to a Period if its date is older than the SNAPSHOT's date of the period.
93    */
94   private static boolean isLineInPeriod(long lineDate, Period period) {
95     return lineDate > period.getSnapshotDate();
96   }
97
98   private Optional<Set<Integer>> getChangedLinesFromReport(Component file) {
99     if (isPullRequestOrShortLivedBranch()) {
100       return reportChangedLinesCache.computeIfAbsent(file, this::readFromReport);
101     }
102
103     return Optional.empty();
104   }
105
106   private boolean isPullRequestOrShortLivedBranch() {
107     return analysisMetadataHolder.isPullRequest() || analysisMetadataHolder.isShortLivingBranch();
108   }
109
110   private Optional<Set<Integer>> readFromReport(Component file) {
111     return reportReader.readComponentChangedLines(file.getReportAttributes().getRef())
112       .map(c -> new HashSet<>(c.getLineList()));
113   }
114 }