]> source.dussan.org Git - sonarqube.git/blob
64cba218e264013c7241c2b8c1822b3c814c0119
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2016 SonarSource SA
4  * mailto:contact AT sonarsource DOT com
5  *
6  * This program 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.
10  *
11  * This program 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.
15  *
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.
19  */
20 package org.sonar.server.computation.formula;
21
22 import com.google.common.base.Optional;
23 import com.google.common.collect.ImmutableList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
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;
38
39 import static java.util.Objects.requireNonNull;
40
41 public class FormulaExecutorComponentVisitor extends PathAwareVisitorAdapter<FormulaExecutorComponentVisitor.Counters> {
42   private static final SimpleStackElementFactory<Counters> COUNTERS_FACTORY = new SimpleStackElementFactory<Counters>() {
43
44     @Override
45     public Counters createForAny(Component component) {
46       return new Counters();
47     }
48
49     @Override
50     public Counters createForFile(Component component) {
51       // No need to create a counter on leaf levels
52       return null;
53     }
54
55     @Override
56     public Counters createForProjectView(Component projectView) {
57       // No need to create a counter on leaf levels
58       return null;
59     }
60   };
61
62   @CheckForNull
63   private final PeriodsHolder periodsHolder;
64   private final MetricRepository metricRepository;
65   private final MeasureRepository measureRepository;
66   private final List<Formula> formulas;
67
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);
74   }
75
76   public static Builder newBuilder(MetricRepository metricRepository, MeasureRepository measureRepository) {
77     return new Builder(metricRepository, measureRepository);
78   }
79
80   public static class Builder {
81     private final MetricRepository metricRepository;
82     private final MeasureRepository measureRepository;
83     @CheckForNull
84     private PeriodsHolder periodsHolder;
85
86     private Builder(MetricRepository metricRepository, MeasureRepository measureRepository) {
87       this.metricRepository = requireNonNull(metricRepository);
88       this.measureRepository = requireNonNull(measureRepository);
89     }
90
91     public Builder create(MetricRepository metricRepository, MeasureRepository measureRepository) {
92       return new Builder(metricRepository, measureRepository);
93     }
94
95     public Builder withVariationSupport(PeriodsHolder periodsHolder) {
96       this.periodsHolder = requireNonNull(periodsHolder);
97       return this;
98     }
99
100     public FormulaExecutorComponentVisitor buildFor(Iterable<Formula> formulas) {
101       return new FormulaExecutorComponentVisitor(this, formulas);
102     }
103   }
104
105   @Override
106   public void visitProject(Component project, Path<FormulaExecutorComponentVisitor.Counters> path) {
107     process(project, path);
108   }
109
110   @Override
111   public void visitModule(Component module, Path<FormulaExecutorComponentVisitor.Counters> path) {
112     process(module, path);
113   }
114
115   @Override
116   public void visitDirectory(Component directory, Path<FormulaExecutorComponentVisitor.Counters> path) {
117     process(directory, path);
118   }
119
120   @Override
121   public void visitFile(Component file, Path<FormulaExecutorComponentVisitor.Counters> path) {
122     process(file, path);
123   }
124
125   @Override
126   public void visitView(Component view, Path<Counters> path) {
127     process(view, path);
128   }
129
130   @Override
131   public void visitSubView(Component subView, Path<Counters> path) {
132     process(subView, path);
133   }
134
135   @Override
136   public void visitProjectView(Component projectView, Path<Counters> path) {
137     process(projectView, path);
138   }
139
140   private void process(Component component, Path<FormulaExecutorComponentVisitor.Counters> path) {
141     if (component.getChildren().isEmpty()) {
142       processLeaf(component, path);
143     } else {
144       processNotLeaf(component, path);
145     }
146   }
147
148   private void processNotLeaf(Component component, Path<FormulaExecutorComponentVisitor.Counters> path) {
149     for (Formula formula : formulas) {
150       Counter counter = path.current().getCounter(formula);
151       // If there were no file under this node, the counter won't be initialized
152       if (counter != null) {
153         for (String metricKey : formula.getOutputMetricKeys()) {
154           addNewMeasure(component, metricKey, formula, counter);
155         }
156         aggregateToParent(path, formula, counter);
157       }
158     }
159   }
160
161   private void processLeaf(Component file, Path<FormulaExecutorComponentVisitor.Counters> path) {
162     CounterInitializationContext counterContext = new CounterInitializationContextImpl(file);
163     for (Formula formula : formulas) {
164       Counter counter = formula.createNewCounter();
165       counter.initialize(counterContext);
166       for (String metricKey : formula.getOutputMetricKeys()) {
167         addNewMeasure(file, metricKey, formula, counter);
168       }
169       aggregateToParent(path, formula, counter);
170     }
171   }
172
173   private void addNewMeasure(Component component, String metricKey, Formula formula, Counter counter) {
174     // no new measure can be created by formulas for PROJECT_VIEW components, their measures are the copy
175     if (component.getType() == Component.Type.PROJECT_VIEW) {
176       return;
177     }
178     Metric metric = metricRepository.getByKey(metricKey);
179     Optional<Measure> measure = formula.createMeasure(counter, new CreateMeasureContextImpl(component, metric));
180     if (measure.isPresent()) {
181       measureRepository.add(component, metric, measure.get());
182     }
183   }
184
185   private void aggregateToParent(Path<FormulaExecutorComponentVisitor.Counters> path, Formula formula, Counter currentCounter) {
186     if (!path.isRoot()) {
187       path.parent().aggregate(formula, currentCounter);
188     }
189   }
190
191   private class CounterInitializationContextImpl implements CounterInitializationContext {
192     private final Component file;
193
194     public CounterInitializationContextImpl(Component file) {
195       this.file = file;
196     }
197
198     @Override
199     public Component getLeaf() {
200       return file;
201     }
202
203     @Override
204     public Optional<Measure> getMeasure(String metricKey) {
205       return measureRepository.getRawMeasure(file, metricRepository.getByKey(metricKey));
206     }
207
208     @Override
209     public List<Period> getPeriods() {
210       return periodsHolder.getPeriods();
211     }
212   }
213
214   public static class Counters {
215     Map<Formula, Counter> countersByFormula = new HashMap<>();
216
217     public void aggregate(Formula formula, Counter childCounter) {
218       Counter counter = countersByFormula.get(formula);
219       if (counter == null) {
220         countersByFormula.put(formula, childCounter);
221       } else {
222         counter.aggregate(childCounter);
223       }
224     }
225
226     /**
227      * Counter can be null on a level when it has not been fed by children levels
228      */
229     @CheckForNull
230     public Counter getCounter(Formula formula) {
231       return countersByFormula.get(formula);
232     }
233   }
234
235   private class CreateMeasureContextImpl implements CreateMeasureContext {
236     private final Component component;
237     private final Metric metric;
238
239     public CreateMeasureContextImpl(Component component, Metric metric) {
240       this.component = component;
241       this.metric = metric;
242     }
243
244     @Override
245     public Component getComponent() {
246       return component;
247     }
248
249     @Override
250     public Metric getMetric() {
251       return metric;
252     }
253
254     @Override
255     public List<Period> getPeriods() {
256       return periodsHolder.getPeriods();
257     }
258   }
259 }