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 }