001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2012 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar 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     * Sonar 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
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
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.batch.index.DefaultIndex;
035    
036    import javax.persistence.Query;
037    
038    import java.util.*;
039    
040    public class DefaultTimeMachine implements TimeMachine {
041    
042      private DatabaseSession session;
043      private DefaultIndex index;
044      private MetricFinder metricFinder;
045    
046      public DefaultTimeMachine(DatabaseSession session, DefaultIndex index, MetricFinder metricFinder) {
047        this.session = session;
048        this.index = index;
049        this.metricFinder = metricFinder;
050      }
051    
052      public List<Measure> getMeasures(TimeMachineQuery query) {
053        Map<Integer, Metric> metricById = getMetricsById(query);
054    
055        List<Object[]> objects = execute(query, true, metricById.keySet());
056        List<Measure> result = Lists.newArrayList();
057    
058        for (Object[] object : objects) {
059          MeasureModel model = (MeasureModel) object[0];
060          Measure measure = toMeasure(model, metricById.get(model.getMetricId()));
061          measure.setDate((Date) object[1]);
062          result.add(measure);
063        }
064        return result;
065      }
066    
067      public List<Object[]> getMeasuresFields(TimeMachineQuery query) {
068        Map<Integer, Metric> metricById = getMetricsById(query);
069        List<Object[]> rows = execute(query, false, metricById.keySet());
070        for (Object[] fields : rows) {
071          fields[1] = metricById.get(fields[1]);
072        }
073        return rows;
074      }
075    
076      protected List execute(TimeMachineQuery query, boolean selectAllFields, Set<Integer> metricIds) {
077        Resource resource = query.getResource();
078        if (resource != null && resource.getId() == null) {
079          resource = index.getResource(query.getResource());
080        }
081        if (resource == null) {
082          return Collections.emptyList();
083        }
084    
085        StringBuilder sb = new StringBuilder();
086        Map<String, Object> params = Maps.newHashMap();
087    
088        if (selectAllFields) {
089          sb.append("SELECT m, s.createdAt ");
090        } else {
091          sb.append("SELECT s.createdAt, m.metricId, m.value ");
092        }
093        sb.append(" FROM ")
094            .append(MeasureModel.class.getSimpleName())
095            .append(" m, ")
096            .append(Snapshot.class.getSimpleName())
097            .append(" s WHERE m.snapshotId=s.id AND s.resourceId=:resourceId AND s.status=:status AND s.qualifier<>:lib");
098        params.put("resourceId", resource.getId());
099        params.put("status", Snapshot.STATUS_PROCESSED);
100        params.put("lib", Qualifiers.LIBRARY);
101    
102        sb.append(" AND m.characteristic IS NULL");
103        sb.append(" AND m.personId IS NULL");
104        sb.append(" AND m.ruleId IS NULL AND m.rulePriority IS NULL");
105        if (!metricIds.isEmpty()) {
106          sb.append(" AND m.metricId IN (:metricIds) ");
107          params.put("metricIds", metricIds);
108        }
109        if (query.isFromCurrentAnalysis()) {
110          sb.append(" AND s.createdAt>=:from ");
111          params.put("from", index.getProject().getAnalysisDate());
112    
113        } else if (query.getFrom() != null) {
114          sb.append(" AND s.createdAt>=:from ");
115          params.put("from", query.getFrom());
116        }
117        if (query.isToCurrentAnalysis()) {
118          sb.append(" AND s.createdAt<=:to ");
119          params.put("to", index.getProject().getAnalysisDate());
120    
121        } else if (query.getTo() != null) {
122          sb.append(" AND s.createdAt<=:to ");
123          params.put("to", query.getTo());
124        }
125        if (query.isOnlyLastAnalysis()) {
126          sb.append(" AND s.last=:last ");
127          params.put("last", Boolean.TRUE);
128        }
129        sb.append(" ORDER BY s.createdAt ");
130    
131        Query jpaQuery = session.createQuery(sb.toString());
132    
133        for (Map.Entry<String, Object> entry : params.entrySet()) {
134          jpaQuery.setParameter(entry.getKey(), entry.getValue());
135        }
136        return jpaQuery.getResultList();
137      }
138    
139      public Map<Integer, Metric> getMetricsById(TimeMachineQuery query) {
140        Collection<Metric> metrics = metricFinder.findAll(query.getMetricKeys());
141        Map<Integer, Metric> result = Maps.newHashMap();
142        for (Metric metric : metrics) {
143          result.put(metric.getId(), metric);
144        }
145        return result;
146      }
147    
148      static Measure toMeasure(MeasureModel model, Metric metric) {
149        // NOTE: measures on rule are not supported
150        Measure measure = new Measure(metric);
151        measure.setId(model.getId());
152        measure.setDescription(model.getDescription());
153        measure.setValue(model.getValue());
154        measure.setData(model.getData(metric));
155        measure.setAlertStatus(model.getAlertStatus());
156        measure.setAlertText(model.getAlertText());
157        measure.setTendency(model.getTendency());
158        measure.setVariation1(model.getVariationValue1());
159        measure.setVariation2(model.getVariationValue2());
160        measure.setVariation3(model.getVariationValue3());
161        measure.setVariation4(model.getVariationValue4());
162        measure.setVariation5(model.getVariationValue5());
163        measure.setUrl(model.getUrl());
164        measure.setCharacteristic(model.getCharacteristic());
165        measure.setPersonId(model.getPersonId());
166        return measure;
167      }
168    }