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.ArrayListMultimap;
023    import com.google.common.collect.ListMultimap;
024    import com.google.common.collect.Lists;
025    import org.sonar.api.batch.DecoratorContext;
026    import org.sonar.api.batch.Event;
027    import org.sonar.api.batch.SonarIndex;
028    import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
029    import org.sonar.api.design.Dependency;
030    import org.sonar.api.measures.CoreMetrics;
031    import org.sonar.api.measures.Measure;
032    import org.sonar.api.measures.MeasuresFilter;
033    import org.sonar.api.measures.MeasuresFilters;
034    import org.sonar.api.measures.Metric;
035    import org.sonar.api.measures.MetricFinder;
036    import org.sonar.api.resources.Project;
037    import org.sonar.api.resources.Resource;
038    import org.sonar.api.rules.Violation;
039    import org.sonar.api.utils.SonarException;
040    import org.sonar.api.violations.ViolationQuery;
041    import org.sonar.batch.duplication.DuplicationCache;
042    import org.sonar.batch.duplication.DuplicationUtils;
043    import org.sonar.batch.scan.measure.MeasureCache;
044    import org.sonar.core.measure.MeasurementFilters;
045    
046    import java.util.Arrays;
047    import java.util.Collection;
048    import java.util.Collections;
049    import java.util.Date;
050    import java.util.List;
051    import java.util.Set;
052    
053    public class DefaultDecoratorContext implements DecoratorContext {
054    
055      private static final String SAVE_MEASURE_METHOD = "saveMeasure";
056      private SonarIndex sonarIndex;
057      private Resource resource;
058      private MeasurementFilters measurementFilters;
059      private boolean readOnly = false;
060    
061      private List<DecoratorContext> childrenContexts;
062    
063      private ListMultimap<String, Measure> measuresByMetric = ArrayListMultimap.create();
064      private MeasureCache measureCache;
065      private MetricFinder metricFinder;
066      private final DuplicationCache duplicationCache;
067    
068      public DefaultDecoratorContext(Resource resource,
069        SonarIndex index,
070        List<DecoratorContext> childrenContexts,
071        MeasurementFilters measurementFilters, MeasureCache measureCache, MetricFinder metricFinder, DuplicationCache duplicationCache) {
072        this.sonarIndex = index;
073        this.resource = resource;
074        this.childrenContexts = childrenContexts;
075        this.measurementFilters = measurementFilters;
076        this.measureCache = measureCache;
077        this.metricFinder = metricFinder;
078        this.duplicationCache = duplicationCache;
079      }
080    
081      public void init() {
082        Iterable<Measure> unfiltered = measureCache.byResource(resource);
083        for (Measure measure : unfiltered) {
084          measuresByMetric.put(measure.getMetricKey(), measure);
085        }
086      }
087    
088      public DefaultDecoratorContext end() {
089        readOnly = true;
090        childrenContexts = null;
091        for (Measure measure : measuresByMetric.values()) {
092          measureCache.put(resource, measure);
093        }
094        return this;
095      }
096    
097      public Project getProject() {
098        return sonarIndex.getProject();
099      }
100    
101      public List<DecoratorContext> getChildren() {
102        checkReadOnly("getModules");
103        return childrenContexts;
104      }
105    
106      private void checkReadOnly(String methodName) {
107        if (readOnly) {
108          throw new IllegalStateException("Method DecoratorContext." + methodName + "() can not be executed on children.");
109        }
110      }
111    
112      public <M> M getMeasures(MeasuresFilter<M> filter) {
113        Collection<Measure> unfiltered;
114        if (filter instanceof MeasuresFilters.MetricFilter) {
115          unfiltered = getMeasuresOfASingleMetric(filter);
116        } else {
117          unfiltered = measuresByMetric.values();
118        }
119        return filter.filter(unfiltered);
120      }
121    
122      private <M> Collection<Measure> getMeasuresOfASingleMetric(MeasuresFilter<M> filter) {
123        Collection<Measure> unfiltered;
124        String metricKey = ((MeasuresFilters.MetricFilter<M>) filter).filterOnMetricKey();
125        if (CoreMetrics.DUPLICATIONS_DATA_KEY.equals(metricKey)) {
126          // Hack for SONAR-5765
127          List<DuplicationGroup> group = duplicationCache.byComponent(resource.getEffectiveKey());
128          if (group != null) {
129            unfiltered = Arrays.asList(new Measure(CoreMetrics.DUPLICATIONS_DATA, DuplicationUtils.toXml(group)));
130          } else {
131            unfiltered = Collections.<Measure>emptyList();
132          }
133        } else {
134          // optimization
135          unfiltered = measuresByMetric.get(metricKey);
136        }
137        return unfiltered;
138      }
139    
140      public Measure getMeasure(Metric metric) {
141        return getMeasures(MeasuresFilters.metric(metric));
142      }
143    
144      public Collection<Measure> getChildrenMeasures(MeasuresFilter filter) {
145        List<Measure> result = Lists.newArrayList();
146        for (DecoratorContext childContext : childrenContexts) {
147          Object childResult = childContext.getMeasures(filter);
148          if (childResult != null) {
149            if (childResult instanceof Collection) {
150              result.addAll((Collection) childResult);
151            } else {
152              result.add((Measure) childResult);
153            }
154          }
155        }
156        return result;
157      }
158    
159      public Collection<Measure> getChildrenMeasures(Metric metric) {
160        return getChildrenMeasures(MeasuresFilters.metric(metric));
161      }
162    
163      public Resource getResource() {
164        return resource;
165      }
166    
167      public DecoratorContext saveMeasure(Measure measure) {
168        checkReadOnly(SAVE_MEASURE_METHOD);
169        Metric metric = metricFinder.findByKey(measure.getMetricKey());
170        if (metric == null) {
171          throw new SonarException("Unknown metric: " + measure.getMetricKey());
172        }
173        measure.setMetric(metric);
174        if (measurementFilters.accept(resource, measure)) {
175          List<Measure> metricMeasures = measuresByMetric.get(measure.getMetricKey());
176    
177          boolean add = true;
178          if (metricMeasures != null) {
179            int index = metricMeasures.indexOf(measure);
180            if (index > -1) {
181              if (metricMeasures.get(index) == measure) {
182                add = false;
183              } else if (measure.getMetric().equals(CoreMetrics.TESTS)) {
184                // Hack for SONAR-5212
185                measuresByMetric.remove(measure.getMetric().getKey(), metricMeasures.get(index));
186              } else {
187                throw new SonarException("Can not add twice the same measure on " + resource + ": " + measure);
188              }
189            }
190          }
191          if (add) {
192            measuresByMetric.put(measure.getMetricKey(), measure);
193          }
194        }
195        return this;
196      }
197    
198      public DecoratorContext saveMeasure(Metric metric, Double value) {
199        checkReadOnly(SAVE_MEASURE_METHOD);
200        saveMeasure(new Measure(metric, value));
201        return this;
202      }
203    
204      /**
205      * {@inheritDoc}
206      */
207      public List<Violation> getViolations(ViolationQuery violationQuery) {
208        return sonarIndex.getViolations(violationQuery);
209      }
210    
211      /**
212      * {@inheritDoc}
213      */
214      public List<Violation> getViolations() {
215        return sonarIndex.getViolations(resource);
216      }
217    
218      public Dependency saveDependency(Dependency dependency) {
219        checkReadOnly("addDependency");
220        return sonarIndex.addDependency(dependency);
221      }
222    
223      public Set<Dependency> getDependencies() {
224        return sonarIndex.getDependencies();
225      }
226    
227      public Collection<Dependency> getIncomingDependencies() {
228        return sonarIndex.getIncomingEdges(resource);
229      }
230    
231      public Collection<Dependency> getOutgoingDependencies() {
232        return sonarIndex.getOutgoingEdges(resource);
233      }
234    
235      public List<Event> getEvents() {
236        return sonarIndex.getEvents(resource);
237      }
238    
239      public Event createEvent(String name, String description, String category, Date date) {
240        return sonarIndex.addEvent(resource, name, description, category, date);
241      }
242    
243      public void deleteEvent(Event event) {
244        sonarIndex.deleteEvent(event);
245      }
246    
247      public DefaultDecoratorContext saveViolation(Violation violation, boolean force) {
248        if (violation.getResource() == null) {
249          violation.setResource(resource);
250        }
251        sonarIndex.addViolation(violation, force);
252        return this;
253      }
254    
255      public DefaultDecoratorContext saveViolation(Violation violation) {
256        return saveViolation(violation, false);
257      }
258    }