]> source.dussan.org Git - sonarqube.git/blob
667ba78579038638b99a291f555f3a264174342c
[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.task.projectanalysis.component;
21
22 import com.google.common.base.Function;
23 import com.google.common.base.Predicate;
24 import com.google.common.collect.ImmutableMap;
25 import com.google.common.collect.Maps;
26 import java.util.List;
27 import java.util.Map;
28 import javax.annotation.Nonnull;
29 import javax.annotation.Nullable;
30 import org.sonar.api.utils.log.Loggers;
31 import org.sonar.core.util.logs.Profiler;
32
33 import static com.google.common.collect.FluentIterable.from;
34 import static com.google.common.collect.Iterables.concat;
35 import static java.util.Objects.requireNonNull;
36
37 /**
38  * This crawler make any number of {@link TypeAwareVisitor} or {@link PathAwareVisitor} defined in a list visit a component tree, component per component, in the order of the list
39  */
40 public class VisitorsCrawler implements ComponentCrawler {
41
42   private final Map<ComponentVisitor, VisitorDuration> visitorCumulativeDurations;
43   private final List<VisitorWrapper> preOrderVisitorWrappers;
44   private final List<VisitorWrapper> postOrderVisitorWrappers;
45
46   public VisitorsCrawler(Iterable<ComponentVisitor> visitors) {
47     List<VisitorWrapper> visitorWrappers = from(visitors).transform(ToVisitorWrapper.INSTANCE).toList();
48     this.preOrderVisitorWrappers = from(visitorWrappers).filter(MathPreOrderVisitor.INSTANCE).toList();
49     this.postOrderVisitorWrappers = from(visitorWrappers).filter(MatchPostOrderVisitor.INSTANCE).toList();
50     this.visitorCumulativeDurations = from(visitors).toMap(VisitorWrapperToInitialDuration.INSTANCE);
51   }
52
53   public Map<ComponentVisitor, Long> getCumulativeDurations() {
54     return ImmutableMap.copyOf(
55       Maps.transformValues(this.visitorCumulativeDurations, VisitorDurationToDuration.INSTANCE)
56       );
57   }
58
59   @Override
60   public void visit(final Component component) {
61     try {
62       visitImpl(component);
63     } catch (RuntimeException e) {
64       VisitException.rethrowOrWrap(
65         e,
66         "Visit of Component {key=%s,type=%s} failed",
67         component.getKey(), component.getType());
68     }
69   }
70
71   private void visitImpl(Component component) {
72     MatchVisitorMaxDepth visitorMaxDepth = MatchVisitorMaxDepth.forComponent(component);
73     List<VisitorWrapper> preOrderVisitorWrappersToExecute = from(preOrderVisitorWrappers).filter(visitorMaxDepth).toList();
74     List<VisitorWrapper> postOrderVisitorWrappersToExecute = from(postOrderVisitorWrappers).filter(visitorMaxDepth).toList();
75     if (preOrderVisitorWrappersToExecute.isEmpty() && postOrderVisitorWrappersToExecute.isEmpty()) {
76       return;
77     }
78
79     for (VisitorWrapper visitorWrapper : concat(preOrderVisitorWrappers, postOrderVisitorWrappers)) {
80       visitorWrapper.beforeComponent(component);
81     }
82
83     for (VisitorWrapper visitorWrapper : preOrderVisitorWrappersToExecute) {
84       visitNode(component, visitorWrapper);
85     }
86
87     visitChildren(component);
88
89     for (VisitorWrapper visitorWrapper : postOrderVisitorWrappersToExecute) {
90       visitNode(component, visitorWrapper);
91     }
92
93     for (VisitorWrapper visitorWrapper : concat(preOrderVisitorWrappersToExecute, postOrderVisitorWrappersToExecute)) {
94       visitorWrapper.afterComponent(component);
95     }
96   }
97
98   private void visitChildren(Component component) {
99     for (Component child : component.getChildren()) {
100       visit(child);
101     }
102   }
103
104   private void visitNode(Component component, VisitorWrapper visitor) {
105     Profiler profiler = Profiler.create(Loggers.get(visitor.getWrappedVisitor().getClass()))
106       .startTrace("Visiting component {}", component.getKey());
107     visitor.visitAny(component);
108     switch (component.getType()) {
109       case PROJECT:
110         visitor.visitProject(component);
111         break;
112       case MODULE:
113         visitor.visitModule(component);
114         break;
115       case DIRECTORY:
116         visitor.visitDirectory(component);
117         break;
118       case FILE:
119         visitor.visitFile(component);
120         break;
121       case VIEW:
122         visitor.visitView(component);
123         break;
124       case SUBVIEW:
125         visitor.visitSubView(component);
126         break;
127       case PROJECT_VIEW:
128         visitor.visitProjectView(component);
129         break;
130       default:
131         throw new IllegalStateException(String.format("Unknown type %s", component.getType().name()));
132     }
133     long duration = profiler.stopTrace();
134     incrementDuration(visitor, duration);
135   }
136
137   private void incrementDuration(VisitorWrapper visitorWrapper, long duration) {
138     visitorCumulativeDurations.get(visitorWrapper.getWrappedVisitor()).increment(duration);
139   }
140
141   private enum ToVisitorWrapper implements Function<ComponentVisitor, VisitorWrapper> {
142     INSTANCE;
143
144     @Override
145     public VisitorWrapper apply(@Nonnull ComponentVisitor componentVisitor) {
146       if (componentVisitor instanceof TypeAwareVisitor) {
147         return new TypeAwareVisitorWrapper((TypeAwareVisitor) componentVisitor);
148       } else if (componentVisitor instanceof PathAwareVisitor) {
149         return new PathAwareVisitorWrapper((PathAwareVisitor) componentVisitor);
150       } else {
151         throw new IllegalArgumentException("Only TypeAwareVisitor and PathAwareVisitor can be used");
152       }
153     }
154   }
155
156   private static class MatchVisitorMaxDepth implements Predicate<VisitorWrapper> {
157     private static final Map<Component.Type, MatchVisitorMaxDepth> INSTANCES = buildInstances();
158
159     private final Component.Type type;
160
161     private static Map<Component.Type, MatchVisitorMaxDepth> buildInstances() {
162       ImmutableMap.Builder<Component.Type, MatchVisitorMaxDepth> builder = ImmutableMap.builder();
163       for (Component.Type type : Component.Type.values()) {
164         builder.put(type, new MatchVisitorMaxDepth(type));
165       }
166       return builder.build();
167     }
168
169     private MatchVisitorMaxDepth(Component.Type type) {
170       this.type = requireNonNull(type);
171     }
172
173     public static MatchVisitorMaxDepth forComponent(Component component) {
174       return INSTANCES.get(component.getType());
175     }
176
177     @Override
178     public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
179       CrawlerDepthLimit maxDepth = visitorWrapper.getMaxDepth();
180       return maxDepth.isSameAs(type) || maxDepth.isDeeperThan(type);
181     }
182   }
183
184   private enum MathPreOrderVisitor implements Predicate<VisitorWrapper> {
185     INSTANCE;
186
187     @Override
188     public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
189       return visitorWrapper.getOrder() == ComponentVisitor.Order.PRE_ORDER;
190     }
191   }
192
193   private enum MatchPostOrderVisitor implements Predicate<VisitorWrapper> {
194     INSTANCE;
195
196     @Override
197     public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
198       return visitorWrapper.getOrder() == ComponentVisitor.Order.POST_ORDER;
199     }
200   }
201
202   private static final class VisitorDuration {
203     private long duration = 0;
204
205     public void increment(long duration) {
206       this.duration += duration;
207     }
208
209     public long getDuration() {
210       return duration;
211     }
212   }
213
214   private enum VisitorWrapperToInitialDuration implements Function<ComponentVisitor, VisitorDuration> {
215     INSTANCE;
216
217     @Override
218     @Nonnull
219     public VisitorDuration apply(@Nonnull ComponentVisitor visitorWrapper) {
220       return new VisitorDuration();
221     }
222   }
223
224   private enum VisitorDurationToDuration implements Function<VisitorDuration, Long> {
225     INSTANCE;
226
227     @Nullable
228     @Override
229     public Long apply(VisitorDuration input) {
230       return input.getDuration();
231     }
232   }
233 }