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    }