001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube 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 * SonarQube 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 com.google.common.base.Preconditions;
023import com.google.common.collect.Maps;
024import org.sonar.api.batch.fs.InputFile;
025import org.sonar.api.batch.fs.internal.DefaultInputFile;
026import org.sonar.api.batch.sensor.coverage.CoverageType;
027import org.sonar.api.batch.sensor.coverage.NewCoverage;
028import org.sonar.api.batch.sensor.internal.DefaultStorable;
029import org.sonar.api.batch.sensor.internal.SensorStorage;
030
031import javax.annotation.Nullable;
032
033import java.util.Collections;
034import java.util.SortedMap;
035
036public class DefaultCoverage extends DefaultStorable implements NewCoverage {
037
038  private DefaultInputFile 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 = Maps.newTreeMap();
044  private SortedMap<Integer, Integer> conditionsByLine = Maps.newTreeMap();
045  private SortedMap<Integer, Integer> coveredConditionsByLine = Maps.newTreeMap();
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 = (DefaultInputFile) inputFile;
058    return this;
059  }
060
061  public InputFile inputFile() {
062    return inputFile;
063  }
064
065  @Override
066  public NewCoverage ofType(CoverageType type) {
067    validateFile();
068    Preconditions.checkNotNull(type, "type can't be null");
069    this.type = type;
070    return this;
071  }
072
073  public CoverageType type() {
074    return type;
075  }
076
077  @Override
078  public NewCoverage lineHits(int line, int hits) {
079    validateFile();
080    validateLine(line);
081
082    if (!hitsByLine.containsKey(line)) {
083      hitsByLine.put(line, hits);
084      if (hits > 0) {
085        totalCoveredLines += 1;
086      }
087    }
088    return this;
089  }
090
091  private void validateLine(int line) {
092    Preconditions.checkState(line <= inputFile.lines(), String.format("Line %d is out of range in the file %s (lines: %d)", line, inputFile.relativePath(), inputFile.lines()));
093    Preconditions.checkState(line > 0, "Line number must be strictly positive: " + line);
094  }
095
096  private void validateLines() {
097    for (int l : hitsByLine.keySet()) {
098      validateLine(l);
099    }
100
101    for (int l : conditionsByLine.keySet()) {
102      validateLine(l);
103    }
104
105    for (int l : coveredConditionsByLine.keySet()) {
106      validateLine(l);
107    }
108  }
109
110  private void validateFile() {
111    Preconditions.checkNotNull(inputFile, "Call onFile() first");
112  }
113
114  @Override
115  public NewCoverage conditions(int line, int conditions, int coveredConditions) {
116    validateFile();
117    validateLine(line);
118
119    if (conditions > 0 && !conditionsByLine.containsKey(line)) {
120      totalConditions += conditions;
121      totalCoveredConditions += coveredConditions;
122      conditionsByLine.put(line, conditions);
123      coveredConditionsByLine.put(line, coveredConditions);
124    }
125    return this;
126  }
127
128  public int coveredLines() {
129    return totalCoveredLines;
130  }
131
132  public int linesToCover() {
133    return hitsByLine.size();
134  }
135
136  public int conditions() {
137    return totalConditions;
138  }
139
140  public int coveredConditions() {
141    return totalCoveredConditions;
142  }
143
144  public SortedMap<Integer, Integer> hitsByLine() {
145    return Collections.unmodifiableSortedMap(hitsByLine);
146  }
147
148  public SortedMap<Integer, Integer> conditionsByLine() {
149    return Collections.unmodifiableSortedMap(conditionsByLine);
150  }
151
152  public SortedMap<Integer, Integer> coveredConditionsByLine() {
153    return Collections.unmodifiableSortedMap(coveredConditionsByLine);
154  }
155
156  @Override
157  public void doSave() {
158    validateFile();
159    Preconditions.checkNotNull(type, "Call ofType() first");
160    validateLines();
161    storage.store(this);
162  }
163
164}