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;
021    
022    import com.google.common.collect.Lists;
023    import com.google.common.collect.Maps;
024    import org.sonar.api.batch.TimeMachine;
025    import org.sonar.api.batch.TimeMachineQuery;
026    import org.sonar.api.database.DatabaseSession;
027    import org.sonar.api.database.model.MeasureModel;
028    import org.sonar.api.database.model.Snapshot;
029    import org.sonar.api.measures.Measure;
030    import org.sonar.api.measures.Metric;
031    import org.sonar.api.measures.MetricFinder;
032    import org.sonar.api.resources.Qualifiers;
033    import org.sonar.api.resources.Resource;
034    import org.sonar.api.technicaldebt.batch.Characteristic;
035    import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
036    import org.sonar.batch.index.DefaultIndex;
037    
038    import javax.annotation.Nullable;
039    import javax.persistence.Query;
040    
041    import java.util.Collection;
042    import java.util.Collections;
043    import java.util.Date;
044    import java.util.List;
045    import java.util.Map;
046    import java.util.Set;
047    
048    public class DefaultTimeMachine implements TimeMachine {
049    
050      private DatabaseSession session;
051      private DefaultIndex index;
052      private MetricFinder metricFinder;
053      private TechnicalDebtModel techDebtModel;
054    
055      public DefaultTimeMachine(DatabaseSession session, DefaultIndex index, MetricFinder metricFinder, TechnicalDebtModel techDebtModel) {
056        this.session = session;
057        this.index = index;
058        this.metricFinder = metricFinder;
059        this.techDebtModel = techDebtModel;
060      }
061    
062      public List<Measure> getMeasures(TimeMachineQuery query) {
063        Map<Integer, Metric> metricById = getMetricsById(query);
064    
065        List<Object[]> objects = execute(query, true, metricById.keySet());
066        List<Measure> result = Lists.newArrayList();
067    
068        for (Object[] object : objects) {
069          MeasureModel model = (MeasureModel) object[0];
070          Integer characteristicId = model.getCharacteristicId();
071          Characteristic characteristic = techDebtModel.characteristicById(characteristicId);
072          Measure measure = toMeasure(model, metricById.get(model.getMetricId()), characteristic);
073          measure.setDate((Date) object[1]);
074          result.add(measure);
075        }
076        return result;
077      }
078    
079      public List<Object[]> getMeasuresFields(TimeMachineQuery query) {
080        Map<Integer, Metric> metricById = getMetricsById(query);
081        List<Object[]> rows = execute(query, false, metricById.keySet());
082        for (Object[] fields : rows) {
083          fields[1] = metricById.get(fields[1]);
084        }
085        return rows;
086      }
087    
088      protected List<Object[]> execute(TimeMachineQuery query, boolean selectAllFields, Set<Integer> metricIds) {
089        Resource resource = query.getResource();
090        if (resource != null && resource.getId() == null) {
091          resource = index.getResource(query.getResource());
092        }
093        if (resource == null) {
094          return Collections.emptyList();
095        }
096    
097        StringBuilder sb = new StringBuilder();
098        Map<String, Object> params = Maps.newHashMap();
099    
100        if (selectAllFields) {
101          sb.append("SELECT m, s.createdAt ");
102        } else {
103          sb.append("SELECT s.createdAt, m.metricId, m.value ");
104        }
105        sb.append(" FROM ")
106          .append(MeasureModel.class.getSimpleName())
107          .append(" m, ")
108          .append(Snapshot.class.getSimpleName())
109          .append(" s WHERE m.snapshotId=s.id AND s.resourceId=:resourceId AND s.status=:status AND s.qualifier<>:lib");
110        params.put("resourceId", resource.getId());
111        params.put("status", Snapshot.STATUS_PROCESSED);
112        params.put("lib", Qualifiers.LIBRARY);
113    
114        sb.append(" AND m.characteristicId IS NULL");
115        sb.append(" AND m.personId IS NULL");
116        sb.append(" AND m.ruleId IS NULL AND m.rulePriority IS NULL");
117        if (!metricIds.isEmpty()) {
118          sb.append(" AND m.metricId IN (:metricIds) ");
119          params.put("metricIds", metricIds);
120        }
121        if (query.isFromCurrentAnalysis()) {
122          sb.append(" AND s.createdAt>=:from ");
123          params.put("from", index.getProject().getAnalysisDate());
124    
125        } else if (query.getFrom() != null) {
126          sb.append(" AND s.createdAt>=:from ");
127          params.put("from", query.getFrom());
128        }
129        if (query.isToCurrentAnalysis()) {
130          sb.append(" AND s.createdAt<=:to ");
131          params.put("to", index.getProject().getAnalysisDate());
132    
133        } else if (query.getTo() != null) {
134          sb.append(" AND s.createdAt<=:to ");
135          params.put("to", query.getTo());
136        }
137        if (query.isOnlyLastAnalysis()) {
138          sb.append(" AND s.last=:last ");
139          params.put("last", Boolean.TRUE);
140        }
141        sb.append(" ORDER BY s.createdAt ");
142    
143        Query jpaQuery = session.createQuery(sb.toString());
144    
145        for (Map.Entry<String, Object> entry : params.entrySet()) {
146          jpaQuery.setParameter(entry.getKey(), entry.getValue());
147        }
148        return jpaQuery.getResultList();
149      }
150    
151      public Map<Integer, Metric> getMetricsById(TimeMachineQuery query) {
152        Collection<Metric> metrics = metricFinder.findAll(query.getMetricKeys());
153        Map<Integer, Metric> result = Maps.newHashMap();
154        for (Metric metric : metrics) {
155          result.put(metric.getId(), metric);
156        }
157        return result;
158      }
159    
160      static Measure toMeasure(MeasureModel model, Metric metric, @Nullable Characteristic characteristic) {
161        // NOTE: measures on rule are not supported
162        Measure measure = new Measure(metric);
163        measure.setDescription(model.getDescription());
164        measure.setValue(model.getValue());
165        measure.setData(model.getData(metric));
166        measure.setAlertStatus(model.getAlertStatus());
167        measure.setAlertText(model.getAlertText());
168        measure.setTendency(model.getTendency());
169        measure.setVariation1(model.getVariationValue1());
170        measure.setVariation2(model.getVariationValue2());
171        measure.setVariation3(model.getVariationValue3());
172        measure.setVariation4(model.getVariationValue4());
173        measure.setVariation5(model.getVariationValue5());
174        measure.setUrl(model.getUrl());
175        measure.setCharacteristic(characteristic);
176        measure.setPersonId(model.getPersonId());
177        return measure;
178      }
179    }