3 * Copyright (C) 2009-2022 SonarSource SA
4 * mailto:info 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.ce.task.projectanalysis.component;
22 import com.google.common.collect.ImmutableMap;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.List;
27 import java.util.function.Function;
28 import java.util.function.Predicate;
29 import java.util.stream.Collectors;
30 import javax.annotation.Nonnull;
31 import org.sonar.api.utils.log.Loggers;
32 import org.sonar.core.util.logs.Profiler;
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 boolean computeDuration;
43 private final Map<ComponentVisitor, VisitorDuration> visitorCumulativeDurations;
44 private final List<VisitorWrapper> preOrderVisitorWrappers;
45 private final List<VisitorWrapper> postOrderVisitorWrappers;
47 public VisitorsCrawler(Collection<ComponentVisitor> visitors) {
48 this(visitors, false);
51 public VisitorsCrawler(Collection<ComponentVisitor> visitors, boolean computeDuration) {
52 List<VisitorWrapper> visitorWrappers = visitors.stream().map(ToVisitorWrapper.INSTANCE).collect(Collectors.toList());
53 this.preOrderVisitorWrappers = visitorWrappers.stream().filter(MathPreOrderVisitor.INSTANCE).collect(Collectors.toList());
54 this.postOrderVisitorWrappers = visitorWrappers.stream().filter(MatchPostOrderVisitor.INSTANCE).collect(Collectors.toList());
55 this.computeDuration = computeDuration;
56 this.visitorCumulativeDurations = computeDuration ? visitors.stream().collect(Collectors.toMap(v -> v, v -> new VisitorDuration())) : Collections.emptyMap();
59 public Map<ComponentVisitor, Long> getCumulativeDurations() {
60 if (computeDuration) {
61 return visitorCumulativeDurations.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getDuration()));
63 return Collections.emptyMap();
67 public void visit(final Component component) {
70 } catch (RuntimeException e) {
71 VisitException.rethrowOrWrap(
73 "Visit of Component {key=%s,type=%s} failed",
74 component.getDbKey(), component.getType());
78 private void visitImpl(Component component) {
79 MatchVisitorMaxDepth visitorMaxDepth = MatchVisitorMaxDepth.forComponent(component);
80 List<VisitorWrapper> preOrderVisitorWrappersToExecute = preOrderVisitorWrappers.stream().filter(visitorMaxDepth).collect(Collectors.toList());
81 List<VisitorWrapper> postOrderVisitorWrappersToExecute = postOrderVisitorWrappers.stream().filter(visitorMaxDepth).collect(Collectors.toList());
82 if (preOrderVisitorWrappersToExecute.isEmpty() && postOrderVisitorWrappersToExecute.isEmpty()) {
86 for (VisitorWrapper visitorWrapper : concat(preOrderVisitorWrappers, postOrderVisitorWrappers)) {
87 visitorWrapper.beforeComponent(component);
90 for (VisitorWrapper visitorWrapper : preOrderVisitorWrappersToExecute) {
91 visitNode(component, visitorWrapper);
94 visitChildren(component);
96 for (VisitorWrapper visitorWrapper : postOrderVisitorWrappersToExecute) {
97 visitNode(component, visitorWrapper);
100 for (VisitorWrapper visitorWrapper : concat(preOrderVisitorWrappersToExecute, postOrderVisitorWrappersToExecute)) {
101 visitorWrapper.afterComponent(component);
105 private void visitChildren(Component component) {
106 for (Component child : component.getChildren()) {
111 private void visitNode(Component component, VisitorWrapper visitor) {
112 Profiler profiler = Profiler.create(Loggers.get(visitor.getWrappedVisitor().getClass()))
113 .startTrace("Visiting component {}", component.getDbKey());
114 visitor.visitAny(component);
115 switch (component.getType()) {
117 visitor.visitProject(component);
120 visitor.visitDirectory(component);
123 visitor.visitFile(component);
126 visitor.visitView(component);
129 visitor.visitSubView(component);
132 visitor.visitProjectView(component);
135 throw new IllegalStateException(String.format("Unknown type %s", component.getType().name()));
137 long duration = profiler.stopTrace();
138 incrementDuration(visitor, duration);
141 private void incrementDuration(VisitorWrapper visitorWrapper, long duration) {
142 if (computeDuration) {
143 visitorCumulativeDurations.get(visitorWrapper.getWrappedVisitor()).increment(duration);
147 private enum ToVisitorWrapper implements Function<ComponentVisitor, VisitorWrapper> {
151 public VisitorWrapper apply(@Nonnull ComponentVisitor componentVisitor) {
152 if (componentVisitor instanceof TypeAwareVisitor) {
153 return new TypeAwareVisitorWrapper((TypeAwareVisitor) componentVisitor);
154 } else if (componentVisitor instanceof PathAwareVisitor) {
155 return new PathAwareVisitorWrapper((PathAwareVisitor) componentVisitor);
157 throw new IllegalArgumentException("Only TypeAwareVisitor and PathAwareVisitor can be used");
162 private static class MatchVisitorMaxDepth implements Predicate<VisitorWrapper> {
163 private static final Map<Component.Type, MatchVisitorMaxDepth> INSTANCES = buildInstances();
164 private final Component.Type type;
166 private MatchVisitorMaxDepth(Component.Type type) {
167 this.type = requireNonNull(type);
170 private static Map<Component.Type, MatchVisitorMaxDepth> buildInstances() {
171 ImmutableMap.Builder<Component.Type, MatchVisitorMaxDepth> builder = ImmutableMap.builder();
172 for (Component.Type type : Component.Type.values()) {
173 builder.put(type, new MatchVisitorMaxDepth(type));
175 return builder.build();
178 public static MatchVisitorMaxDepth forComponent(Component component) {
179 return INSTANCES.get(component.getType());
183 public boolean test(VisitorWrapper visitorWrapper) {
184 CrawlerDepthLimit maxDepth = visitorWrapper.getMaxDepth();
185 return maxDepth.isSameAs(type) || maxDepth.isDeeperThan(type);
189 private enum MathPreOrderVisitor implements Predicate<VisitorWrapper> {
193 public boolean test(VisitorWrapper visitorWrapper) {
194 return visitorWrapper.getOrder() == ComponentVisitor.Order.PRE_ORDER;
198 private enum MatchPostOrderVisitor implements Predicate<VisitorWrapper> {
202 public boolean test(VisitorWrapper visitorWrapper) {
203 return visitorWrapper.getOrder() == ComponentVisitor.Order.POST_ORDER;
207 private static final class VisitorDuration {
208 private long duration = 0;
210 public void increment(long duration) {
211 this.duration += duration;
214 public long getDuration() {