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 */
020 package org.sonar.batch.scan;
021
022 import org.sonar.api.batch.Sensor;
023 import org.sonar.api.batch.fs.FileSystem;
024 import org.sonar.api.batch.fs.InputDir;
025 import org.sonar.api.batch.fs.InputFile;
026 import org.sonar.api.batch.fs.InputPath;
027 import org.sonar.api.batch.measure.Metric;
028 import org.sonar.api.batch.rule.ActiveRules;
029 import org.sonar.api.batch.sensor.SensorContext;
030 import org.sonar.api.batch.sensor.issue.Issue;
031 import org.sonar.api.batch.sensor.measure.Measure;
032 import org.sonar.api.component.ResourcePerspectives;
033 import org.sonar.api.config.Settings;
034 import org.sonar.api.issue.Issuable;
035 import org.sonar.api.issue.internal.DefaultIssue;
036 import org.sonar.api.measures.Formula;
037 import org.sonar.api.measures.MetricFinder;
038 import org.sonar.api.measures.PersistenceMode;
039 import org.sonar.api.measures.SumChildDistributionFormula;
040 import org.sonar.api.resources.Directory;
041 import org.sonar.api.resources.File;
042 import org.sonar.api.resources.Project;
043 import org.sonar.api.resources.Resource;
044 import org.sonar.api.resources.Scopes;
045 import org.sonar.api.rule.RuleKey;
046 import org.sonar.batch.duplication.BlockCache;
047 import org.sonar.batch.duplication.DuplicationCache;
048 import org.sonar.batch.index.ComponentDataCache;
049 import org.sonar.batch.scan2.BaseSensorContext;
050
051 import java.io.Serializable;
052
053 /**
054 * Implements {@link SensorContext} but forward everything to {@link org.sonar.api.batch.SensorContext} for backward compatibility.
055 * Will be dropped once old {@link Sensor} API is dropped.
056 *
057 */
058 public class SensorContextAdaptor extends BaseSensorContext {
059
060 private final org.sonar.api.batch.SensorContext sensorContext;
061 private final MetricFinder metricFinder;
062 private final Project project;
063 private final ResourcePerspectives perspectives;
064
065 public SensorContextAdaptor(org.sonar.api.batch.SensorContext sensorContext, MetricFinder metricFinder, Project project, ResourcePerspectives perspectives,
066 Settings settings, FileSystem fs, ActiveRules activeRules, ComponentDataCache componentDataCache, BlockCache blockCache,
067 DuplicationCache duplicationCache) {
068 super(settings, fs, activeRules, componentDataCache, blockCache, duplicationCache);
069 this.sensorContext = sensorContext;
070 this.metricFinder = metricFinder;
071 this.project = project;
072 this.perspectives = perspectives;
073 }
074
075 @Override
076 public Measure getMeasure(String metricKey) {
077 Metric<?> m = findMetricOrFail(metricKey);
078 return getMeasure(m);
079 }
080
081 @Override
082 public <G extends Serializable> Measure<G> getMeasure(Metric<G> metric) {
083 org.sonar.api.measures.Metric<G> m = (org.sonar.api.measures.Metric<G>) findMetricOrFail(metric.key());
084 org.sonar.api.measures.Measure<G> measure = sensorContext.getMeasure(m);
085 if (measure == null) {
086 return null;
087 }
088 return this.<G>measureBuilder()
089 .onProject()
090 .forMetric(metric)
091 .withValue(measure.value())
092 .build();
093 }
094
095 @Override
096 public Measure getMeasure(InputFile file, String metricKey) {
097 Metric<?> m = findMetricOrFail(metricKey);
098 return getMeasure(file, m);
099 }
100
101 private Metric findMetricOrFail(String metricKey) {
102 Metric<?> m = metricFinder.findByKey(metricKey);
103 if (m == null) {
104 throw new IllegalStateException("Unknow metric with key: " + metricKey);
105 }
106 return m;
107 }
108
109 @Override
110 public <G extends Serializable> Measure<G> getMeasure(InputFile file, Metric<G> metric) {
111 File fileRes = File.create(file.relativePath());
112 org.sonar.api.measures.Metric<G> m = (org.sonar.api.measures.Metric<G>) findMetricOrFail(metric.key());
113 org.sonar.api.measures.Measure<G> measure = sensorContext.getMeasure(fileRes, m);
114 if (measure == null) {
115 return null;
116 }
117 return this.<G>measureBuilder()
118 .onFile(file)
119 .forMetric(metric)
120 .withValue(measure.value())
121 .build();
122 }
123
124 @Override
125 public void addMeasure(Measure<?> measure) {
126 org.sonar.api.measures.Metric<?> m = metricFinder.findByKey(measure.metric().key());
127 if (m == null) {
128 throw new IllegalStateException("Unknow metric with key: " + measure.metric().key());
129 }
130
131 org.sonar.api.measures.Measure measureToSave = new org.sonar.api.measures.Measure(m);
132 setValueAccordingToMetricType(measure, m, measureToSave);
133 if (measure.inputFile() != null) {
134 Formula formula = measure.metric() instanceof org.sonar.api.measures.Metric ?
135 ((org.sonar.api.measures.Metric) measure.metric()).getFormula() : null;
136 if (formula instanceof SumChildDistributionFormula
137 && !Scopes.isHigherThanOrEquals(Scopes.FILE, ((SumChildDistributionFormula) formula).getMinimumScopeToPersist())) {
138 measureToSave.setPersistenceMode(PersistenceMode.MEMORY);
139 }
140 sensorContext.saveMeasure(measure.inputFile(), measureToSave);
141 } else {
142 sensorContext.saveMeasure(measureToSave);
143 }
144 }
145
146 private void setValueAccordingToMetricType(Measure<?> measure, org.sonar.api.measures.Metric<?> m, org.sonar.api.measures.Measure measureToSave) {
147 switch (m.getType()) {
148 case BOOL:
149 measureToSave.setValue(Boolean.TRUE.equals(measure.value()) ? 1.0 : 0.0);
150 break;
151 case INT:
152 case MILLISEC:
153 measureToSave.setValue(Double.valueOf((Integer) measure.value()));
154 break;
155 case FLOAT:
156 case PERCENT:
157 case RATING:
158 measureToSave.setValue((Double) measure.value());
159 break;
160 case STRING:
161 case LEVEL:
162 case DATA:
163 case DISTRIB:
164 measureToSave.setData((String) measure.value());
165 break;
166 case WORK_DUR:
167 measureToSave.setValue(Double.valueOf((Long) measure.value()));
168 break;
169 default:
170 if (m.isNumericType()) {
171 measureToSave.setValue((Double) measure.value());
172 } else if (m.isDataType()) {
173 measureToSave.setData((String) measure.value());
174 } else {
175 throw new UnsupportedOperationException("Unsupported type :" + m.getType());
176 }
177 }
178 }
179
180 @Override
181 public boolean addIssue(Issue issue) {
182 Resource r;
183 InputPath inputPath = issue.inputPath();
184 if (inputPath != null) {
185 if (inputPath instanceof InputDir) {
186 r = Directory.create(inputPath.relativePath());
187 } else {
188 r = File.create(inputPath.relativePath());
189 }
190 } else {
191 r = project;
192 }
193 Issuable issuable = perspectives.as(Issuable.class, r);
194 if (issuable == null) {
195 return false;
196 }
197 return issuable.addIssue(toDefaultIssue(project.getKey(), r.getKey(), issue));
198 }
199
200 public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) {
201 return new org.sonar.core.issue.DefaultIssueBuilder()
202 .componentKey(componentKey)
203 .projectKey(projectKey)
204 .ruleKey(RuleKey.of(issue.ruleKey().repository(), issue.ruleKey().rule()))
205 .effortToFix(issue.effortToFix())
206 .line(issue.line())
207 .message(issue.message())
208 .severity(issue.severity())
209 .build();
210 }
211
212 }