]> source.dussan.org Git - sonarqube.git/blob
37ae2e227e5410e9a3ab515c1a167f02550ce68f
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 SonarSource SA
4  * mailto:info 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.ce.task.projectanalysis.component;
21
22 import com.google.common.base.Function;
23 import com.google.common.base.Joiner;
24 import javax.annotation.Nonnull;
25 import javax.annotation.concurrent.Immutable;
26
27 import static com.google.common.collect.FluentIterable.from;
28 import static java.lang.String.format;
29 import static java.util.Objects.requireNonNull;
30 import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
31 import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
32
33 /**
34  * A {@link ComponentCrawler} which provide access to a representation of the path from the root to the currently visited
35  * Component. It also provides a way to have an object associated to each Component and access it and all of its
36  * parent's.
37  * As for {@link DepthTraversalTypeAwareCrawler}, this crawler supports max depth visit and ordering.
38  */
39 public final class PathAwareCrawler<T> implements ComponentCrawler {
40   private final PathAwareVisitor<T> visitor;
41   private final DequeBasedPath<T> stack = new DequeBasedPath<>();
42
43   public PathAwareCrawler(PathAwareVisitor<T> visitor) {
44     this.visitor = requireNonNull(visitor);
45   }
46
47   @Override
48   public void visit(Component component) {
49     try {
50       visitImpl(component);
51     } catch (RuntimeException e) {
52       VisitException.rethrowOrWrap(
53         e,
54         "Visit failed for Component {key=%s,type=%s} %s",
55         component.getDbKey(), component.getType(), new ComponentPathPrinter<>(stack));
56     }
57   }
58
59   private void visitImpl(Component component) {
60     if (!verifyDepth(component)) {
61       return;
62     }
63
64     stack.add(new PathElementImpl<>(component, createForComponent(component)));
65
66     if (this.visitor.getOrder() == PRE_ORDER) {
67       visitNode(component);
68     }
69
70     visitChildren(component);
71
72     if (this.visitor.getOrder() == POST_ORDER) {
73       visitNode(component);
74     }
75
76     stack.pop();
77   }
78
79   private boolean verifyDepth(Component component) {
80     CrawlerDepthLimit maxDepth = this.visitor.getMaxDepth();
81     return maxDepth.isSameAs(component.getType()) || maxDepth.isDeeperThan(component.getType());
82   }
83
84   private void visitChildren(Component component) {
85     for (Component child : component.getChildren()) {
86       if (verifyDepth(component)) {
87         visit(child);
88       }
89     }
90   }
91
92   private void visitNode(Component component) {
93     this.visitor.visitAny(component, stack);
94     switch (component.getType()) {
95       case PROJECT:
96         this.visitor.visitProject(component, stack);
97         break;
98       case DIRECTORY:
99         this.visitor.visitDirectory(component, stack);
100         break;
101       case FILE:
102         this.visitor.visitFile(component, stack);
103         break;
104       case VIEW:
105         this.visitor.visitView(component, stack);
106         break;
107       case SUBVIEW:
108         this.visitor.visitSubView(component, stack);
109         break;
110       case PROJECT_VIEW:
111         this.visitor.visitProjectView(component, stack);
112         break;
113       default:
114         throw new IllegalArgumentException(format("Unsupported component type %s, no visitor method to call", component.getType()));
115     }
116   }
117
118   private T createForComponent(Component component) {
119     switch (component.getType()) {
120       case PROJECT:
121         return this.visitor.getFactory().createForProject(component);
122       case DIRECTORY:
123         return this.visitor.getFactory().createForDirectory(component);
124       case FILE:
125         return this.visitor.getFactory().createForFile(component);
126       case VIEW:
127         return this.visitor.getFactory().createForView(component);
128       case SUBVIEW:
129         return this.visitor.getFactory().createForSubView(component);
130       case PROJECT_VIEW:
131         return this.visitor.getFactory().createForProjectView(component);
132       default:
133         throw new IllegalArgumentException(format("Unsupported component type %s, can not create stack object", component.getType()));
134     }
135   }
136
137   /**
138    * A simple object wrapping the currentPath allowing to compute the string representing the path only if
139    * the VisitException is actually built (ie. method {@link ComponentPathPrinter#toString()} is called
140    * by the internal {@link String#format(String, Object...)} of
141    * {@link VisitException#rethrowOrWrap(RuntimeException, String, Object...)}.
142    */
143   @Immutable
144   private static final class ComponentPathPrinter<T> {
145
146     private static final Joiner PATH_ELEMENTS_JOINER = Joiner.on("->");
147
148     private final DequeBasedPath<T> currentPath;
149
150     private ComponentPathPrinter(DequeBasedPath<T> currentPath) {
151       this.currentPath = currentPath;
152     }
153
154     @Override
155     public String toString() {
156       if (currentPath.isRoot()) {
157         return "";
158       }
159       return " located " + toKeyPath(currentPath);
160     }
161
162     private static <T> String toKeyPath(Iterable<PathAwareVisitor.PathElement<T>> currentPath) {
163       return PATH_ELEMENTS_JOINER.join(from(currentPath).transform(PathElementToComponentAsString.INSTANCE).skip(1));
164     }
165
166     private enum PathElementToComponentAsString implements Function<PathAwareVisitor.PathElement<?>, String> {
167       INSTANCE;
168
169       @Override
170       @Nonnull
171       public String apply(@Nonnull PathAwareVisitor.PathElement<?> input) {
172         return format("%s(type=%s)", input.getComponent().getDbKey(), input.getComponent().getType());
173       }
174     }
175   }
176
177 }