From: Sébastien Lesaint Date: Wed, 24 Jun 2015 14:16:10 +0000 (+0200) Subject: SONAR-6664 add PathAwareVisitor to visit a Component tree X-Git-Tag: 5.2-RC1~1309 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d4c10655a1bba2681b4597deda42c61a8b7c5f99;p=sonarqube.git SONAR-6664 add PathAwareVisitor to visit a Component tree --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentVisitor.java index 98e02828f48..5804e0b2b55 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentVisitor.java @@ -21,4 +21,8 @@ package org.sonar.server.computation.component; public interface ComponentVisitor { void visit(Component tree); + + enum Order { + PRE_ORDER, POST_ORDER + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java index c43c9cbd4ee..7ff708046b5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java @@ -53,7 +53,7 @@ public abstract class DepthTraversalTypeAwareVisitor implements TypeAwareVisitor } } - protected void visitNode(Component component) { + private void visitNode(Component component) { visitAny(component); switch (component.getType()) { case PROJECT: @@ -110,8 +110,4 @@ public abstract class DepthTraversalTypeAwareVisitor implements TypeAwareVisitor public void visitAny(Component component) { // empty implementation, meant to be override at will by subclasses } - - public enum Order { - PRE_ORDER, POST_ORDER - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/PathAwareVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/PathAwareVisitor.java new file mode 100644 index 00000000000..b8114ca02cf --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/PathAwareVisitor.java @@ -0,0 +1,295 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.computation.component; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import static java.util.Objects.requireNonNull; +import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; + +/** + * A ComponentVisitor which provide access to a representation of the path from the root to the currently visited + * Component. It also provides a way to have an object associated to each Component and access it and all of its + * parent's. + * As for {@link DepthTraversalTypeAwareVisitor}, this visitor supports max depth visit and ordering. + */ +public abstract class PathAwareVisitor implements ComponentVisitor { + private final Component.Type maxDepth; + private final Order order; + private final StackElementFactory factory; + private final DequeBasedPath stack = new DequeBasedPath<>(); + + public PathAwareVisitor(Component.Type maxDepth, Order order, StackElementFactory factory) { + this.maxDepth = requireNonNull(maxDepth); + this.order = requireNonNull(order); + this.factory = requireNonNull(factory, "Factory can not be null"); + } + + @Override + public void visit(Component component) { + if (component.getType().isDeeperThan(maxDepth)) { + return; + } + + stack.add(new PathElementImpl<>(component, createForComponent(component))); + + if (order == PRE_ORDER) { + visitNode(component); + } + + visitChildren(component); + + if (order == POST_ORDER) { + visitNode(component); + } + + stack.pop(); + } + + private T createForComponent(Component component) { + switch (component.getType()) { + case PROJECT: + return factory.createForProject(component); + case MODULE: + return factory.createForModule(component); + case DIRECTORY: + return factory.createForDirectory(component); + case FILE: + return factory.createForFile(component); + default: + return factory.createForUnknown(component); + } + } + + private void visitChildren(Component component) { + if (component.getType() != maxDepth) { + for (Component child : component.getChildren()) { + visit(child); + } + } + } + + private void visitNode(Component component) { + visitAny(component, stack); + switch (component.getType()) { + case PROJECT: + visitProject(component, stack); + break; + case MODULE: + visitModule(component, stack); + break; + case DIRECTORY: + visitDirectory(component, stack); + break; + case FILE: + visitFile(component, stack); + break; + default: + visitUnknown(component, stack); + } + } + + protected void visitProject(Component project, Path path) { + // empty implementation, meant to be override at will by subclasses + } + + protected void visitModule(Component module, Path path) { + // empty implementation, meant to be override at will by subclasses + } + + protected void visitDirectory(Component directory, Path path) { + // empty implementation, meant to be override at will by subclasses + } + + protected void visitFile(Component file, Path path) { + // empty implementation, meant to be override at will by subclasses + } + + protected void visitUnknown(Component unknownComponent, Path path) { + // empty implementation, meant to be override at will by subclasses + } + + protected void visitAny(Component component, Path path) { + // empty implementation, meant to be override at will by subclasses + } + + public interface StackElementFactory { + T createForProject(Component project); + + T createForModule(Component module); + + T createForDirectory(Component directory); + + T createForFile(Component file); + + T createForUnknown(Component file); + } + + /** + * A Simple implementation which uses the same factory method for all types which can be implemented by subclasses: + * {@link #createForAny(Component)}. + */ + public abstract static class SimpleStackElementFactory implements StackElementFactory { + + public abstract T createForAny(Component component); + + @Override + public T createForProject(Component project) { + return createForAny(project); + } + + @Override + public T createForModule(Component module) { + return createForAny(module); + } + + @Override + public T createForDirectory(Component directory) { + return createForAny(directory); + } + + @Override + public T createForFile(Component file) { + return createForAny(file); + } + + @Override + public T createForUnknown(Component file) { + return createForAny(file); + } + } + + private static class DequeBasedPath implements Path, Iterable> { + private final Deque> deque = new ArrayDeque<>(); + + @Override + public T current() { + return deque.getFirst().getElement(); + } + + @Override + public T parent() { + Iterator> iterator = deque.iterator(); + if (iterator.hasNext()) { + iterator.next(); + if (iterator.hasNext()) { + return iterator.next().getElement(); + } + } + throw new NoSuchElementException("Path is either empty or has only one element. There is no parent"); + } + + @Override + public boolean isRoot() { + return deque.size() == 1; + } + + @Override + public T root() { + return deque.getLast().getElement(); + } + + @Override + public Iterator> iterator() { + return deque.iterator(); + } + + @Override + public Iterable> getCurrentPath() { + return this; + } + + public void add(PathElement pathElement) { + deque.addFirst(pathElement); + } + + public PathElement pop() { + return deque.pop(); + } + } + + public interface Path { + /** + * The stacked element of the current Component. + */ + T current(); + + /** + * Tells whether the current Component is the root of the tree. + */ + boolean isRoot(); + + /** + * The stacked element of the parent of the current Component. + * + * @throws NoSuchElementException if the current Component is the root of the tree + * @see #isRoot() + */ + T parent(); + + /** + * The stacked element of the root of the tree. + */ + T root(); + + /** + * The path to the current Component as an Iterable of {@link PathAwareVisitor.PathElement} which starts with + * the {@link PathAwareVisitor.PathElement} of the current Component and ends with the + * {@link PathAwareVisitor.PathElement} of the root of the tree. + */ + Iterable> getCurrentPath(); + } + + public interface PathElement { + /** + * The Component on the path. + */ + Component getComponent(); + + /** + * The stacked element for the Component of this PathElement. + */ + T getElement(); + } + + private static final class PathElementImpl implements PathElement { + private final Component component; + private final T element; + + private PathElementImpl(Component component, T element) { + this.component = component; + this.element = element; + } + + @Override + public Component getComponent() { + return component; + } + + @Override + public T getElement() { + return element; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java index fc0aee4129c..3aa32ff0c12 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java @@ -23,7 +23,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.POST_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER; /** * Holds the reference to the root of the {@link Component} tree for the current CE run. diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeIssueMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeIssueMeasuresStep.java index 1fd30585f08..f7060ab065b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeIssueMeasuresStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeIssueMeasuresStep.java @@ -69,7 +69,7 @@ import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY; import static org.sonar.server.computation.component.Component.Type.FILE; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.POST_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER; /** * Computes metrics related to number of issues. diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStep.java index cf3e7ef1ca8..c41d928ac5d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStep.java @@ -46,7 +46,7 @@ import org.sonar.server.computation.period.Period; import org.sonar.server.computation.period.PeriodsHolder; import org.sonar.server.db.DbClient; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; /** * Set variations on all numeric measures found in the repository. diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java index 1baae898213..7e069c2dc5f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java @@ -36,7 +36,7 @@ import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.db.DbClient; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; /** * Persist duplications into diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java index 523f4a3d608..8ab4833ba68 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java @@ -49,7 +49,7 @@ import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDb; import org.sonar.server.util.CloseableIterator; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; public class PersistFileSourcesStep implements ComputationStep { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistMeasuresStep.java index 46a21ac6b90..a148c0ecb6a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistMeasuresStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistMeasuresStep.java @@ -44,7 +44,7 @@ import org.sonar.server.computation.metric.MetricRepository; import org.sonar.server.db.DbClient; import static com.google.common.collect.FluentIterable.from; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; public class PersistMeasuresStep implements ComputationStep { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java index 2b3866ce742..da5a180b7fa 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java @@ -39,7 +39,7 @@ import org.sonar.server.db.DbClient; import org.sonar.server.source.index.SourceLineIndex; import static com.google.common.base.Objects.firstNonNull; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; public class PersistNumberOfDaysSinceLastCommitStep implements ComputationStep { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistProjectLinksStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistProjectLinksStep.java index 949c73698de..64d5eff98b6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistProjectLinksStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistProjectLinksStep.java @@ -41,7 +41,7 @@ import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.db.DbClient; import static com.google.common.collect.Sets.newHashSet; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; /** * Persist project and module links diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateLoadingStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateLoadingStep.java index 4cb2afa9154..22e4f9787bc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateLoadingStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateLoadingStep.java @@ -34,7 +34,7 @@ import org.sonar.server.computation.qualitygate.QualityGate; import org.sonar.server.computation.qualitygate.QualityGateService; import static org.sonar.server.computation.component.Component.Type.PROJECT; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; /** * This step retrieves the QualityGate for the current {@link ReportQueue.Item} and stores it in diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateMeasuresStep.java index 1ebbc1f3fb3..bc9406c721a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateMeasuresStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateMeasuresStep.java @@ -44,7 +44,7 @@ import org.sonar.server.computation.qualitygate.QualityGate; import org.sonar.server.computation.qualitygate.QualityGateHolder; import static org.sonar.server.computation.component.Component.Type.PROJECT; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; /** * This step: diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java index d02909069d3..18447724fab 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java @@ -42,7 +42,7 @@ import org.sonar.server.computation.metric.MetricRepository; import org.sonar.server.computation.qualityprofile.QPMeasureData; import org.sonar.server.computation.qualityprofile.QualityProfile; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.POST_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER; public class QualityProfileEventsStep implements ComputationStep { private final TreeRootHolder treeRootHolder; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java index c715cfbabb2..17b066cd5ff 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java @@ -29,7 +29,7 @@ import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.computation.component.TreeRootHolder; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.POST_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER; public class TreeRootHolderRule implements TestRule, TreeRootHolder { private Component root; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/PathAwareVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/PathAwareVisitorTest.java new file mode 100644 index 00000000000..4130a79b850 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/PathAwareVisitorTest.java @@ -0,0 +1,264 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.computation.component; + +import com.google.common.base.Function; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.junit.Test; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.FluentIterable.from; +import static com.google.common.collect.ImmutableList.of; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.server.computation.component.Component.Type.DIRECTORY; +import static org.sonar.server.computation.component.Component.Type.FILE; +import static org.sonar.server.computation.component.Component.Type.MODULE; +import static org.sonar.server.computation.component.Component.Type.PROJECT; +import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; + +public class PathAwareVisitorTest { + + private static final int ROOT_REF = 1; + private static final DumbComponent SOME_TREE_ROOT = DumbComponent.builder(PROJECT, ROOT_REF) + .addChildren( + DumbComponent.builder(MODULE, 11) + .addChildren( + DumbComponent.builder(DIRECTORY, 111) + .addChildren( + DumbComponent.builder(FILE, 1111).build(), + DumbComponent.builder(FILE, 1112).build() + ) + .build(), + DumbComponent.builder(DIRECTORY, 112) + .addChildren( + DumbComponent.builder(FILE, 1121).build() + ) + .build()) + .build(), + DumbComponent.builder(MODULE, 12) + .addChildren( + DumbComponent.builder(DIRECTORY, 121) + .addChildren( + DumbComponent.builder(FILE, 1211).build() + ) + .build() + ).build() + ).build(); + + @Test + public void verify_preOrder() { + TestPathAwareVisitor underTest = new TestPathAwareVisitor(PRE_ORDER); + underTest.visit(SOME_TREE_ROOT); + + Iterator expected = of( + newCallRecord("visitAny", 1, null, of(1)), + newCallRecord("visitProject", 1, null, of(1)), + newCallRecord("visitAny", 11, 1, of(11, 1)), + newCallRecord("visitModule", 11, 1, of(11, 1)), + newCallRecord("visitAny", 111, 11, of(111, 11, 1)), + newCallRecord("visitDirectory", 111, 11, of(111, 11, 1)), + newCallRecord("visitAny", 1111, 111, of(1111, 111, 11, 1)), + newCallRecord("visitFile", 1111, 111, of(1111, 111, 11, 1)), + newCallRecord("visitAny", 1112, 111, of(1112, 111, 11, 1)), + newCallRecord("visitFile", 1112, 111, of(1112, 111, 11, 1)), + newCallRecord("visitAny", 112, 11, of(112, 11, 1)), + newCallRecord("visitDirectory", 112, 11, of(112, 11, 1)), + newCallRecord("visitAny", 1121, 112, of(1121, 112, 11, 1)), + newCallRecord("visitFile", 1121, 112, of(1121, 112, 11, 1)), + newCallRecord("visitAny", 12, 1, of(12, 1)), + newCallRecord("visitModule", 12, 1, of(12, 1)), + newCallRecord("visitAny", 121, 12, of(121, 12, 1)), + newCallRecord("visitDirectory", 121, 12, of(121, 12, 1)), + newCallRecord("visitAny", 1211, 121, of(1211, 121, 12, 1)), + newCallRecord("visitFile", 1211, 121, of(1211, 121, 12, 1)) + ).iterator(); + verifyCallRecords(expected, underTest.callsRecords.iterator()); + } + + @Test + public void verify_postOrder() { + TestPathAwareVisitor underTest = new TestPathAwareVisitor(POST_ORDER); + underTest.visit(SOME_TREE_ROOT); + + Iterator expected = of( + newCallRecord("visitAny", 1111, 111, of(1111, 111, 11, 1)), + newCallRecord("visitFile", 1111, 111, of(1111, 111, 11, 1)), + newCallRecord("visitAny", 1112, 111, of(1112, 111, 11, 1)), + newCallRecord("visitFile", 1112, 111, of(1112, 111, 11, 1)), + newCallRecord("visitAny", 111, 11, of(111, 11, 1)), + newCallRecord("visitDirectory", 111, 11, of(111, 11, 1)), + newCallRecord("visitAny", 1121, 112, of(1121, 112, 11, 1)), + newCallRecord("visitFile", 1121, 112, of(1121, 112, 11, 1)), + newCallRecord("visitAny", 112, 11, of(112, 11, 1)), + newCallRecord("visitDirectory", 112, 11, of(112, 11, 1)), + newCallRecord("visitAny", 11, 1, of(11, 1)), + newCallRecord("visitModule", 11, 1, of(11, 1)), + newCallRecord("visitAny", 1211, 121, of(1211, 121, 12, 1)), + newCallRecord("visitFile", 1211, 121, of(1211, 121, 12, 1)), + newCallRecord("visitAny", 121, 12, of(121, 12, 1)), + newCallRecord("visitDirectory", 121, 12, of(121, 12, 1)), + newCallRecord("visitAny", 12, 1, of(12, 1)), + newCallRecord("visitModule", 12, 1, of(12, 1)), + newCallRecord("visitAny", 1, null, of(1)), + newCallRecord("visitProject", 1, null, of(1)) + ).iterator(); + verifyCallRecords(expected, underTest.callsRecords.iterator()); + } + + private static void verifyCallRecords(Iterator expected, Iterator actual) { + while (expected.hasNext()) { + assertThat(actual.next()).isEqualTo(expected.next()); + } + } + + private static CallRecord newCallRecord(String method, int currentRef, @Nullable Integer parentRef, List path) { + return new CallRecord(method, currentRef, currentRef, parentRef, ROOT_REF, path); + } + + private static class TestPathAwareVisitor extends PathAwareVisitor { + private final List callsRecords = new ArrayList<>(); + + public TestPathAwareVisitor(ComponentVisitor.Order order) { + super(FILE, order, new SimpleStackElementFactory() { + @Override + public Integer createForAny(Component component) { + return component.getRef(); + } + }); + } + + @Override + protected void visitProject(Component project, Path path) { + callsRecords.add(newCallRecord(project, path, "visitProject")); + } + + @Override + protected void visitModule(Component module, Path path) { + callsRecords.add(newCallRecord(module, path, "visitModule")); + } + + @Override + protected void visitDirectory(Component directory, Path path) { + callsRecords.add(newCallRecord(directory, path, "visitDirectory")); + } + + @Override + protected void visitFile(Component file, Path path) { + callsRecords.add(newCallRecord(file, path, "visitFile")); + } + + @Override + protected void visitUnknown(Component unknownComponent, Path path) { + callsRecords.add(newCallRecord(unknownComponent, path, "visitUnknown")); + } + + @Override + protected void visitAny(Component component, Path path) { + callsRecords.add(newCallRecord(component, path, "visitAny")); + } + + private static CallRecord newCallRecord(Component project, Path path, String method) { + return new CallRecord(method, project.getRef(), path.current(), getParent(path), path.root(), + toValueList(path)); + } + + private static List toValueList(Path path) { + return from(path.getCurrentPath()).transform(new Function, Integer>() { + @Nonnull + @Override + public Integer apply(@Nonnull PathElement input) { + return input.getElement(); + } + }).toList(); + } + + private static Integer getParent(Path path) { + try { + Integer parent = path.parent(); + checkArgument(parent != null, "Path.parent returned a null value!"); + return parent; + } catch (NoSuchElementException e) { + return null; + } + } + } + + private static class CallRecord { + private final String method; + private final int ref; + private final int current; + @CheckForNull + private final Integer parent; + private final int root; + private final List path; + + private CallRecord(String method, int ref, int current, @Nullable Integer parent, int root, List path) { + this.method = method; + this.ref = ref; + this.current = current; + this.parent = parent; + this.root = root; + this.path = path; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CallRecord that = (CallRecord) o; + return Objects.equals(ref, that.ref) && + Objects.equals(current, that.current) && + Objects.equals(root, that.root) && + Objects.equals(method, that.method) && + Objects.equals(parent, that.parent) && + Objects.equals(path, that.path); + } + + @Override + public int hashCode() { + return Objects.hash(method, ref, current, parent, root, path); + } + + @Override + public String toString() { + return "{" + + "method='" + method + '\'' + + ", ref=" + ref + + ", current=" + current + + ", parent=" + parent + + ", root=" + root + + ", path=" + path + + '}'; + } + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/PostOrderDepthTraversalTypeAwareVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/PostOrderDepthTraversalTypeAwareVisitorTest.java index 48e57e23f76..3e3514b115b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/PostOrderDepthTraversalTypeAwareVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/PostOrderDepthTraversalTypeAwareVisitorTest.java @@ -29,7 +29,7 @@ import static org.sonar.server.computation.component.Component.Type.DIRECTORY; import static org.sonar.server.computation.component.Component.Type.FILE; import static org.sonar.server.computation.component.Component.Type.MODULE; import static org.sonar.server.computation.component.Component.Type.PROJECT; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.POST_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER; public class PostOrderDepthTraversalTypeAwareVisitorTest { diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/PreOrderDepthTraversalTypeAwareVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/PreOrderDepthTraversalTypeAwareVisitorTest.java index c471d2341fc..f2129085694 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/PreOrderDepthTraversalTypeAwareVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/PreOrderDepthTraversalTypeAwareVisitorTest.java @@ -29,7 +29,7 @@ import static org.sonar.server.computation.component.Component.Type.DIRECTORY; import static org.sonar.server.computation.component.Component.Type.FILE; import static org.sonar.server.computation.component.Component.Type.MODULE; import static org.sonar.server.computation.component.Component.Type.PROJECT; -import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; +import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; public class PreOrderDepthTraversalTypeAwareVisitorTest {