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.plugins.core.timemachine;
021    
022    import com.google.common.collect.ArrayListMultimap;
023    import com.google.common.collect.ListMultimap;
024    import com.google.common.collect.Lists;
025    import org.apache.commons.lang.StringUtils;
026    import org.apache.commons.lang.time.DateUtils;
027    import org.sonar.api.batch.*;
028    import org.sonar.api.measures.Measure;
029    import org.sonar.api.measures.Metric;
030    import org.sonar.api.measures.MetricFinder;
031    import org.sonar.api.resources.Project;
032    import org.sonar.api.resources.Resource;
033    import org.sonar.api.resources.Scopes;
034    import org.sonar.batch.components.TimeMachineConfiguration;
035    import org.sonar.core.NotDryRun;
036    
037    import java.util.List;
038    
039    @NotDryRun
040    @DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE)
041    public class TendencyDecorator implements Decorator {
042    
043      public static final String PROP_DAYS_DESCRIPTION = "Number of days the tendency should be calculated on.";
044    
045      private TimeMachine timeMachine;
046      private TimeMachineQuery query;
047      private TendencyAnalyser analyser;
048      private TimeMachineConfiguration configuration;
049      private List<Metric> metrics;
050    
051      public TendencyDecorator(TimeMachine timeMachine, MetricFinder metricFinder, TimeMachineConfiguration configuration) {
052        this.timeMachine = timeMachine;
053        this.analyser = new TendencyAnalyser();
054        this.configuration = configuration;
055        this.metrics = Lists.newLinkedList();
056        for (Metric metric : metricFinder.findAll()) {
057          if (metric.isNumericType()) {
058            metrics.add(metric);
059          }
060        }
061      }
062    
063      TendencyDecorator(TimeMachine timeMachine, TimeMachineQuery query, TendencyAnalyser analyser, TimeMachineConfiguration configuration) {
064        this.timeMachine = timeMachine;
065        this.query = query;
066        this.analyser = analyser;
067        this.configuration = configuration;
068      }
069    
070      @DependsUpon
071      public List<Metric> dependsUponMetrics() {
072        return metrics;
073      }
074    
075      protected TimeMachineQuery initQuery(Project project) {
076        int days = configuration.getTendencyPeriodInDays();
077    
078        query = new TimeMachineQuery(null) // resource is set after
079            .setFrom(DateUtils.addDays(project.getAnalysisDate(), -days))
080            .setToCurrentAnalysis(true)
081            .setMetrics(metrics);
082        return query;
083      }
084    
085      protected TimeMachineQuery resetQuery(Project project, Resource resource) {
086        if (query == null) {
087          initQuery(project);
088        }
089        query.setResource(resource);
090        return query;
091      }
092    
093      public boolean shouldExecuteOnProject(Project project) {
094        return !configuration.skipTendencies();
095      }
096    
097      public void decorate(Resource resource, DecoratorContext context) {
098        if (shouldDecorateResource(resource)) {
099          resetQuery(context.getProject(), resource);
100          List<Object[]> fields = timeMachine.getMeasuresFields(query);
101          ListMultimap<Metric, Double> valuesPerMetric = ArrayListMultimap.create();
102          for (Object[] field : fields) {
103            valuesPerMetric.put((Metric) field[1], (Double) field[2]);
104          }
105    
106          for (Metric metric : query.getMetrics()) {
107            Measure measure = context.getMeasure(metric);
108            if (measure != null) {
109              List<Double> values = valuesPerMetric.get(metric);
110              values.add(measure.getValue());
111    
112              measure.setTendency(analyser.analyseLevel(valuesPerMetric.get(metric)));
113              context.saveMeasure(measure);
114            }
115          }
116        }
117      }
118    
119      private boolean shouldDecorateResource(Resource resource) {
120        return StringUtils.equals(Scopes.PROJECT, resource.getScope()) || StringUtils.equals(Scopes.DIRECTORY, resource.getScope());
121      }
122    }