2 * SonarQube, open source software quality management tool.
3 * Copyright (C) 2008-2014 SonarSource
4 * mailto:contact AT sonarsource DOT com
6 * SonarQube is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * SonarQube is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.computation.formula;
22 import com.google.common.base.Optional;
23 import com.google.common.collect.ImmutableList;
24 import java.util.HashMap;
25 import java.util.List;
27 import javax.annotation.CheckForNull;
28 import org.sonar.server.computation.component.Component;
29 import org.sonar.server.computation.component.ComponentVisitor;
30 import org.sonar.server.computation.component.CrawlerDepthLimit;
31 import org.sonar.server.computation.component.PathAwareVisitorAdapter;
32 import org.sonar.server.computation.measure.Measure;
33 import org.sonar.server.computation.measure.MeasureRepository;
34 import org.sonar.server.computation.metric.Metric;
35 import org.sonar.server.computation.metric.MetricRepository;
36 import org.sonar.server.computation.period.Period;
37 import org.sonar.server.computation.period.PeriodsHolder;
39 import static java.util.Objects.requireNonNull;
41 public class FormulaExecutorComponentVisitor extends PathAwareVisitorAdapter<FormulaExecutorComponentVisitor.Counters> {
42 private static final SimpleStackElementFactory<Counters> COUNTERS_FACTORY = new SimpleStackElementFactory<Counters>() {
45 public Counters createForAny(Component component) {
46 return new Counters();
50 public Counters createForFile(Component component) {
51 // No need to create a counter on leaf levels
56 public Counters createForProjectView(Component projectView) {
57 // No need to create a counter on leaf levels
63 private final PeriodsHolder periodsHolder;
64 private final MetricRepository metricRepository;
65 private final MeasureRepository measureRepository;
66 private final List<Formula> formulas;
68 private FormulaExecutorComponentVisitor(Builder builder, Iterable<Formula> formulas) {
69 super(CrawlerDepthLimit.LEAVES, ComponentVisitor.Order.POST_ORDER, COUNTERS_FACTORY);
70 this.periodsHolder = builder.periodsHolder;
71 this.measureRepository = builder.measureRepository;
72 this.metricRepository = builder.metricRepository;
73 this.formulas = ImmutableList.copyOf(formulas);
76 public static Builder newBuilder(MetricRepository metricRepository, MeasureRepository measureRepository) {
77 return new Builder(metricRepository, measureRepository);
80 public static class Builder {
81 private final MetricRepository metricRepository;
82 private final MeasureRepository measureRepository;
84 private PeriodsHolder periodsHolder;
86 private Builder(MetricRepository metricRepository, MeasureRepository measureRepository) {
87 this.metricRepository = requireNonNull(metricRepository);
88 this.measureRepository = requireNonNull(measureRepository);
91 public Builder create(MetricRepository metricRepository, MeasureRepository measureRepository) {
92 return new Builder(metricRepository, measureRepository);
95 public Builder withVariationSupport(PeriodsHolder periodsHolder) {
96 this.periodsHolder = requireNonNull(periodsHolder);
100 public FormulaExecutorComponentVisitor buildFor(Iterable<Formula> formulas) {
101 return new FormulaExecutorComponentVisitor(this, formulas);
106 public void visitProject(Component project, Path<FormulaExecutorComponentVisitor.Counters> path) {
107 processNotLeaf(project, path);
111 public void visitModule(Component module, Path<FormulaExecutorComponentVisitor.Counters> path) {
112 processNotLeaf(module, path);
116 public void visitDirectory(Component directory, Path<FormulaExecutorComponentVisitor.Counters> path) {
117 processNotLeaf(directory, path);
121 public void visitFile(Component file, Path<FormulaExecutorComponentVisitor.Counters> path) {
122 processLeaf(file, path);
126 public void visitView(Component view, Path<Counters> path) {
127 processNotLeaf(view, path);
131 public void visitSubView(Component subView, Path<Counters> path) {
132 processNotLeaf(subView, path);
136 public void visitProjectView(Component projectView, Path<Counters> path) {
137 processLeaf(projectView, path);
140 private void processNotLeaf(Component component, Path<FormulaExecutorComponentVisitor.Counters> path) {
141 for (Formula formula : formulas) {
142 Counter counter = path.current().getCounter(formula);
143 // If there were no file under this node, the counter won't be initialized
144 if (counter != null) {
145 for (String metricKey : formula.getOutputMetricKeys()) {
146 addNewMeasure(component, metricKey, formula, counter);
148 aggregateToParent(path, formula, counter);
153 private void processLeaf(Component file, Path<FormulaExecutorComponentVisitor.Counters> path) {
154 CounterInitializationContext counterContext = new CounterInitializationContextImpl(file);
155 for (Formula formula : formulas) {
156 Counter counter = formula.createNewCounter();
157 counter.initialize(counterContext);
158 for (String metricKey : formula.getOutputMetricKeys()) {
159 addNewMeasure(file, metricKey, formula, counter);
161 aggregateToParent(path, formula, counter);
165 private void addNewMeasure(Component component, String metricKey, Formula formula, Counter counter) {
166 // no new measure can be created by formulas for PROJECT_VIEW components, their measures are the copy
167 if (component.getType() == Component.Type.PROJECT_VIEW) {
170 Metric metric = metricRepository.getByKey(metricKey);
171 Optional<Measure> measure = formula.createMeasure(counter, new CreateMeasureContextImpl(component, metric));
172 if (measure.isPresent()) {
173 measureRepository.add(component, metric, measure.get());
177 private void aggregateToParent(Path<FormulaExecutorComponentVisitor.Counters> path, Formula formula, Counter currentCounter) {
178 if (!path.isRoot()) {
179 path.parent().aggregate(formula, currentCounter);
183 private class CounterInitializationContextImpl implements CounterInitializationContext {
184 private final Component file;
186 public CounterInitializationContextImpl(Component file) {
191 public Component getLeaf() {
196 public Optional<Measure> getMeasure(String metricKey) {
197 return measureRepository.getRawMeasure(file, metricRepository.getByKey(metricKey));
201 public List<Period> getPeriods() {
202 return periodsHolder.getPeriods();
206 public static class Counters {
207 Map<Formula, Counter> countersByFormula = new HashMap<>();
209 public void aggregate(Formula formula, Counter childCounter) {
210 Counter counter = countersByFormula.get(formula);
211 if (counter == null) {
212 countersByFormula.put(formula, childCounter);
214 counter.aggregate(childCounter);
219 * Counter can be null on a level when it has not been fed by children levels
222 public Counter getCounter(Formula formula) {
223 return countersByFormula.get(formula);
227 private class CreateMeasureContextImpl implements CreateMeasureContext {
228 private final Component component;
229 private final Metric metric;
231 public CreateMeasureContextImpl(Component component, Metric metric) {
232 this.component = component;
233 this.metric = metric;
237 public Component getComponent() {
242 public Metric getMetric() {
247 public List<Period> getPeriods() {
248 return periodsHolder.getPeriods();