001/*
002 * SonarQube
003 * Copyright (C) 2009-2017 SonarSource SA
004 * mailto:info AT sonarsource DOT com
005 *
006 * This program is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.api.batch.sensor.coverage.internal;
021
022import java.util.Collections;
023import java.util.SortedMap;
024import java.util.TreeMap;
025import javax.annotation.Nullable;
026import org.sonar.api.batch.fs.InputFile;
027import org.sonar.api.batch.fs.internal.DefaultInputFile;
028import org.sonar.api.batch.sensor.coverage.CoverageType;
029import org.sonar.api.batch.sensor.coverage.NewCoverage;
030import org.sonar.api.batch.sensor.internal.DefaultStorable;
031import org.sonar.api.batch.sensor.internal.SensorStorage;
032
033import static com.google.common.base.Preconditions.checkState;
034import static java.util.Objects.requireNonNull;
035
036public class DefaultCoverage extends DefaultStorable implements NewCoverage {
037
038  private InputFile inputFile;
039  private CoverageType type;
040  private int totalCoveredLines = 0;
041  private int totalConditions = 0;
042  private int totalCoveredConditions = 0;
043  private SortedMap<Integer, Integer> hitsByLine = new TreeMap<>();
044  private SortedMap<Integer, Integer> conditionsByLine = new TreeMap<>();
045  private SortedMap<Integer, Integer> coveredConditionsByLine = new TreeMap<>();
046
047  public DefaultCoverage() {
048    super();
049  }
050
051  public DefaultCoverage(@Nullable SensorStorage storage) {
052    super(storage);
053  }
054
055  @Override
056  public DefaultCoverage onFile(InputFile inputFile) {
057    this.inputFile = inputFile;
058    return this;
059  }
060
061  public InputFile inputFile() {
062    return inputFile;
063  }
064
065  @Override
066  public NewCoverage ofType(CoverageType type) {
067    this.type = requireNonNull(type, "type can't be null");
068    return this;
069  }
070
071  public CoverageType type() {
072    return type;
073  }
074
075  @Override
076  public NewCoverage lineHits(int line, int hits) {
077    validateFile();
078    if (isExcluded()) {
079      return this;
080    }
081    validateLine(line);
082
083    if (!hitsByLine.containsKey(line)) {
084      hitsByLine.put(line, hits);
085      if (hits > 0) {
086        totalCoveredLines += 1;
087      }
088    }
089    return this;
090  }
091
092  private void validateLine(int line) {
093    checkState(line <= inputFile.lines(), "Line %s is out of range in the file %s (lines: %s)", line, inputFile, inputFile.lines());
094    checkState(line > 0, "Line number must be strictly positive: %s", line);
095  }
096
097  private void validateFile() {
098    requireNonNull(inputFile, "Call onFile() first");
099  }
100
101  @Override
102  public NewCoverage conditions(int line, int conditions, int coveredConditions) {
103    validateFile();
104    if (isExcluded()) {
105      return this;
106    }
107    validateLine(line);
108
109    if (conditions > 0 && !conditionsByLine.containsKey(line)) {
110      totalConditions += conditions;
111      totalCoveredConditions += coveredConditions;
112      conditionsByLine.put(line, conditions);
113      coveredConditionsByLine.put(line, coveredConditions);
114    }
115    return this;
116  }
117
118  public int coveredLines() {
119    return totalCoveredLines;
120  }
121
122  public int linesToCover() {
123    return hitsByLine.size();
124  }
125
126  public int conditions() {
127    return totalConditions;
128  }
129
130  public int coveredConditions() {
131    return totalCoveredConditions;
132  }
133
134  public SortedMap<Integer, Integer> hitsByLine() {
135    return Collections.unmodifiableSortedMap(hitsByLine);
136  }
137
138  public SortedMap<Integer, Integer> conditionsByLine() {
139    return Collections.unmodifiableSortedMap(conditionsByLine);
140  }
141
142  public SortedMap<Integer, Integer> coveredConditionsByLine() {
143    return Collections.unmodifiableSortedMap(coveredConditionsByLine);
144  }
145
146  @Override
147  public void doSave() {
148    validateFile();
149    if (!isExcluded()) {
150      storage.store(this);
151    }
152  }
153
154  private boolean isExcluded() {
155    return ((DefaultInputFile) inputFile).isExcludedForCoverage();
156  }
157
158}