]> source.dussan.org Git - sonarqube.git/blob
4cc724156672e0dce4755b2382bbabdbeacd205b
[sonarqube.git] /
1 /*
2  * SonarQube, open source software quality management tool.
3  * Copyright (C) 2008-2014 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
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.
10  *
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.
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     processNotLeaf(project, path);
108   }
109
110   @Override
111   public void visitModule(Component module, Path<FormulaExecutorComponentVisitor.Counters> path) {
112     processNotLeaf(module, path);
113   }
114
115   @Override
116   public void visitDirectory(Component directory, Path<FormulaExecutorComponentVisitor.Counters> path) {
117     processNotLeaf(directory, path);
118   }
119
120   @Override
121   public void visitFile(Component file, Path<FormulaExecutorComponentVisitor.Counters> path) {
122     processLeaf(file, path);
123   }
124
125   @Override
126   public void visitView(Component view, Path<Counters> path) {
127     processNotLeaf(view, path);
128   }
129
130   @Override
131   public void visitSubView(Component subView, Path<Counters> path) {
132     processNotLeaf(subView, path);
133   }
134
135   @Override
136   public void visitProjectView(Component projectView, Path<Counters> path) {
137     processLeaf(projectView, path);
138   }
139
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);
147         }
148         aggregateToParent(path, formula, counter);
149       }
150     }
151   }
152
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);
160       }
161       aggregateToParent(path, formula, counter);
162     }
163   }
164
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) {
168       return;
169     }
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());
174     }
175   }
176
177   private void aggregateToParent(Path<FormulaExecutorComponentVisitor.Counters> path, Formula formula, Counter currentCounter) {
178     if (!path.isRoot()) {
179       path.parent().aggregate(formula, currentCounter);
180     }
181   }
182
183   private class CounterInitializationContextImpl implements CounterInitializationContext {
184     private final Component file;
185
186     public CounterInitializationContextImpl(Component file) {
187       this.file = file;
188     }
189
190     @Override
191     public Component getLeaf() {
192       return file;
193     }
194
195     @Override
196     public Optional<Measure> getMeasure(String metricKey) {
197       return measureRepository.getRawMeasure(file, metricRepository.getByKey(metricKey));
198     }
199
200     @Override
201     public List<Period> getPeriods() {
202       return periodsHolder.getPeriods();
203     }
204   }
205
206   public static class Counters {
207     Map<Formula, Counter> countersByFormula = new HashMap<>();
208
209     public void aggregate(Formula formula, Counter childCounter) {
210       Counter counter = countersByFormula.get(formula);
211       if (counter == null) {
212         countersByFormula.put(formula, childCounter);
213       } else {
214         counter.aggregate(childCounter);
215       }
216     }
217
218     /**
219      * Counter can be null on a level when it has not been fed by children levels
220      */
221     @CheckForNull
222     public Counter getCounter(Formula formula) {
223       return countersByFormula.get(formula);
224     }
225   }
226
227   private class CreateMeasureContextImpl implements CreateMeasureContext {
228     private final Component component;
229     private final Metric metric;
230
231     public CreateMeasureContextImpl(Component component, Metric metric) {
232       this.component = component;
233       this.metric = metric;
234     }
235
236     @Override
237     public Component getComponent() {
238       return component;
239     }
240
241     @Override
242     public Metric getMetric() {
243       return metric;
244     }
245
246     @Override
247     public List<Period> getPeriods() {
248       return periodsHolder.getPeriods();
249     }
250   }
251 }