3 * Copyright (C) 2009-2016 SonarSource SA
4 * mailto:contact AT sonarsource DOT com
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.
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.
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.task.projectanalysis.component;
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;
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;
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;
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
40 public class VisitorsCrawler implements ComponentCrawler {
42 private final Map<ComponentVisitor, VisitorDuration> visitorCumulativeDurations;
43 private final List<VisitorWrapper> preOrderVisitorWrappers;
44 private final List<VisitorWrapper> postOrderVisitorWrappers;
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);
53 public Map<ComponentVisitor, Long> getCumulativeDurations() {
54 return ImmutableMap.copyOf(
55 Maps.transformValues(this.visitorCumulativeDurations, VisitorDurationToDuration.INSTANCE)
60 public void visit(final Component component) {
63 } catch (RuntimeException e) {
64 VisitException.rethrowOrWrap(
66 "Visit of Component {key=%s,type=%s} failed",
67 component.getKey(), component.getType());
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()) {
79 for (VisitorWrapper visitorWrapper : concat(preOrderVisitorWrappers, postOrderVisitorWrappers)) {
80 visitorWrapper.beforeComponent(component);
83 for (VisitorWrapper visitorWrapper : preOrderVisitorWrappersToExecute) {
84 visitNode(component, visitorWrapper);
87 visitChildren(component);
89 for (VisitorWrapper visitorWrapper : postOrderVisitorWrappersToExecute) {
90 visitNode(component, visitorWrapper);
93 for (VisitorWrapper visitorWrapper : concat(preOrderVisitorWrappersToExecute, postOrderVisitorWrappersToExecute)) {
94 visitorWrapper.afterComponent(component);
98 private void visitChildren(Component component) {
99 for (Component child : component.getChildren()) {
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()) {
110 visitor.visitProject(component);
113 visitor.visitModule(component);
116 visitor.visitDirectory(component);
119 visitor.visitFile(component);
122 visitor.visitView(component);
125 visitor.visitSubView(component);
128 visitor.visitProjectView(component);
131 throw new IllegalStateException(String.format("Unknown type %s", component.getType().name()));
133 long duration = profiler.stopTrace();
134 incrementDuration(visitor, duration);
137 private void incrementDuration(VisitorWrapper visitorWrapper, long duration) {
138 visitorCumulativeDurations.get(visitorWrapper.getWrappedVisitor()).increment(duration);
141 private enum ToVisitorWrapper implements Function<ComponentVisitor, VisitorWrapper> {
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);
151 throw new IllegalArgumentException("Only TypeAwareVisitor and PathAwareVisitor can be used");
156 private static class MatchVisitorMaxDepth implements Predicate<VisitorWrapper> {
157 private static final Map<Component.Type, MatchVisitorMaxDepth> INSTANCES = buildInstances();
159 private final Component.Type type;
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));
166 return builder.build();
169 private MatchVisitorMaxDepth(Component.Type type) {
170 this.type = requireNonNull(type);
173 public static MatchVisitorMaxDepth forComponent(Component component) {
174 return INSTANCES.get(component.getType());
178 public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
179 CrawlerDepthLimit maxDepth = visitorWrapper.getMaxDepth();
180 return maxDepth.isSameAs(type) || maxDepth.isDeeperThan(type);
184 private enum MathPreOrderVisitor implements Predicate<VisitorWrapper> {
188 public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
189 return visitorWrapper.getOrder() == ComponentVisitor.Order.PRE_ORDER;
193 private enum MatchPostOrderVisitor implements Predicate<VisitorWrapper> {
197 public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
198 return visitorWrapper.getOrder() == ComponentVisitor.Order.POST_ORDER;
202 private static final class VisitorDuration {
203 private long duration = 0;
205 public void increment(long duration) {
206 this.duration += duration;
209 public long getDuration() {
214 private enum VisitorWrapperToInitialDuration implements Function<ComponentVisitor, VisitorDuration> {
219 public VisitorDuration apply(@Nonnull ComponentVisitor visitorWrapper) {
220 return new VisitorDuration();
224 private enum VisitorDurationToDuration implements Function<VisitorDuration, Long> {
229 public Long apply(VisitorDuration input) {
230 return input.getDuration();