]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6664 add PathAwareVisitor to visit a Component tree
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 24 Jun 2015 14:16:10 +0000 (16:16 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 26 Jun 2015 13:10:27 +0000 (15:10 +0200)
18 files changed:
server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentVisitor.java
server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java
server/sonar-server/src/main/java/org/sonar/server/computation/component/PathAwareVisitor.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeIssueMeasuresStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistMeasuresStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistProjectLinksStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateLoadingStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateMeasuresStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java
server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/component/PathAwareVisitorTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/component/PostOrderDepthTraversalTypeAwareVisitorTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/component/PreOrderDepthTraversalTypeAwareVisitorTest.java

index 98e02828f48b9d9c1d77e195831f408c111a5276..5804e0b2b553b562c02ac90a7e0ae46c5d0f24ec 100644 (file)
@@ -21,4 +21,8 @@ package org.sonar.server.computation.component;
 
 public interface ComponentVisitor {
   void visit(Component tree);
+
+  enum Order {
+    PRE_ORDER, POST_ORDER
+  }
 }
index c43c9cbd4eeeb8018241d2bf0a802da806b287c6..7ff708046b59badd05a583d23007ff069fbc14e0 100644 (file)
@@ -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 (file)
index 0000000..b8114ca
--- /dev/null
@@ -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<T> implements ComponentVisitor {
+  private final Component.Type maxDepth;
+  private final Order order;
+  private final StackElementFactory<T> factory;
+  private final DequeBasedPath<T> stack = new DequeBasedPath<>();
+
+  public PathAwareVisitor(Component.Type maxDepth, Order order, StackElementFactory<T> 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<T> path) {
+    // empty implementation, meant to be override at will by subclasses
+  }
+
+  protected void visitModule(Component module, Path<T> path) {
+    // empty implementation, meant to be override at will by subclasses
+  }
+
+  protected void visitDirectory(Component directory, Path<T> path) {
+    // empty implementation, meant to be override at will by subclasses
+  }
+
+  protected void visitFile(Component file, Path<T> path) {
+    // empty implementation, meant to be override at will by subclasses
+  }
+
+  protected void visitUnknown(Component unknownComponent, Path<T> path) {
+    // empty implementation, meant to be override at will by subclasses
+  }
+
+  protected void visitAny(Component component, Path<T> path) {
+    // empty implementation, meant to be override at will by subclasses
+  }
+
+  public interface StackElementFactory<T> {
+    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<T> implements StackElementFactory<T> {
+
+    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<T> implements Path<T>, Iterable<PathElement<T>> {
+    private final Deque<PathElement<T>> deque = new ArrayDeque<>();
+
+    @Override
+    public T current() {
+      return deque.getFirst().getElement();
+    }
+
+    @Override
+    public T parent() {
+      Iterator<PathElement<T>> 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<PathElement<T>> iterator() {
+      return deque.iterator();
+    }
+
+    @Override
+    public Iterable<PathElement<T>> getCurrentPath() {
+      return this;
+    }
+
+    public void add(PathElement<T> pathElement) {
+      deque.addFirst(pathElement);
+    }
+
+    public PathElement<T> pop() {
+      return deque.pop();
+    }
+  }
+
+  public interface Path<T> {
+    /**
+     * 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<PathElement<T>> getCurrentPath();
+  }
+
+  public interface PathElement<T> {
+    /**
+     * The Component on the path.
+     */
+    Component getComponent();
+
+    /**
+     * The stacked element for the Component of this PathElement.
+     */
+    T getElement();
+  }
+
+  private static final class PathElementImpl<T> implements PathElement<T> {
+    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;
+    }
+  }
+}
index fc0aee4129ce750d39e8952dd66d00363ddc8393..3aa32ff0c121492167b778df38f9c180be00f964 100644 (file)
@@ -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.
index 1fd30585f084c1ce396f8a42238c4b9130ae91b5..f7060ab065bf66ce12788b717a4b4a875f4436dc 100644 (file)
@@ -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.
index cf3e7ef1ca89ad0d04b498c6fe8d48ae8c92f246..c41d928ac5d4b7fd97f07040eb60ccf1c3bbba56 100644 (file)
@@ -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.
index 1baae89821300cf67b392b68e4b6ad5e0ad2c34d..7e069c2dc5f2d31d2d22026ba83316aa9cc940c0 100644 (file)
@@ -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
index 523f4a3d608cd604add4b2d0ad0f01371b19f0c6..8ab4833ba687929dd4a5bfa8361489a1bf5eb5d7 100644 (file)
@@ -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 {
 
index 46a21ac6b901cb538eca110047d5005a1f758cac..a148c0ecb6a83dcd55683db40e3da248a46e45bc 100644 (file)
@@ -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 {
 
index 2b3866ce74256bfbe53de58d1c077ef83d67f1df..da5a180b7fa20bf802a95453cfc97870a472fe96 100644 (file)
@@ -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 {
 
index 949c73698de8875ec28e2fb1945cd9ca102aa8dd..64d5eff98b641a1e2bab288d5245e3ca62cd8637 100644 (file)
@@ -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
index 4cb2afa91546085a0b4b07409609074f587ca5e5..22e4f9787bce520a81c8788a7d6acc0b259bf562 100644 (file)
@@ -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
index 1ebbc1f3fb31057c0782fb4f3fcf8d87d7b25cad..bc9406c721afda541c10b5fe94640a7e82b3c3c1 100644 (file)
@@ -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:
index d02909069d3e8b13d35eb2729bfdc299c767a7be..18447724fab24611e36a0393263704a7d12d97c3 100644 (file)
@@ -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;
index c715cfbabb2f1dafb340d2981398288f215618b1..17b066cd5ff79a0c10ce2fdd8814a39508f1de38 100644 (file)
@@ -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 (file)
index 0000000..4130a79
--- /dev/null
@@ -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<CallRecord> 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<CallRecord> 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<CallRecord> expected, Iterator<CallRecord> actual) {
+    while (expected.hasNext()) {
+      assertThat(actual.next()).isEqualTo(expected.next());
+    }
+  }
+
+  private static CallRecord newCallRecord(String method, int currentRef, @Nullable Integer parentRef, List<Integer> path) {
+    return new CallRecord(method, currentRef, currentRef, parentRef, ROOT_REF, path);
+  }
+
+  private static class TestPathAwareVisitor extends PathAwareVisitor<Integer> {
+    private final List<CallRecord> callsRecords = new ArrayList<>();
+
+    public TestPathAwareVisitor(ComponentVisitor.Order order) {
+      super(FILE, order, new SimpleStackElementFactory<Integer>() {
+        @Override
+        public Integer createForAny(Component component) {
+          return component.getRef();
+        }
+      });
+    }
+
+    @Override
+    protected void visitProject(Component project, Path<Integer> path) {
+      callsRecords.add(newCallRecord(project, path, "visitProject"));
+    }
+
+    @Override
+    protected void visitModule(Component module, Path<Integer> path) {
+      callsRecords.add(newCallRecord(module, path, "visitModule"));
+    }
+
+    @Override
+    protected void visitDirectory(Component directory, Path<Integer> path) {
+      callsRecords.add(newCallRecord(directory, path, "visitDirectory"));
+    }
+
+    @Override
+    protected void visitFile(Component file, Path<Integer> path) {
+      callsRecords.add(newCallRecord(file, path, "visitFile"));
+    }
+
+    @Override
+    protected void visitUnknown(Component unknownComponent, Path<Integer> path) {
+      callsRecords.add(newCallRecord(unknownComponent, path, "visitUnknown"));
+    }
+
+    @Override
+    protected void visitAny(Component component, Path<Integer> path) {
+      callsRecords.add(newCallRecord(component, path, "visitAny"));
+    }
+
+    private static CallRecord newCallRecord(Component project, Path<Integer> path, String method) {
+      return new CallRecord(method, project.getRef(), path.current(), getParent(path), path.root(),
+        toValueList(path));
+    }
+
+    private static List<Integer> toValueList(Path<Integer> path) {
+      return from(path.getCurrentPath()).transform(new Function<PathElement<Integer>, Integer>() {
+        @Nonnull
+        @Override
+        public Integer apply(@Nonnull PathElement<Integer> input) {
+          return input.getElement();
+        }
+      }).toList();
+    }
+
+    private static Integer getParent(Path<Integer> 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<Integer> path;
+
+    private CallRecord(String method, int ref, int current, @Nullable Integer parent, int root, List<Integer> 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 +
+        '}';
+    }
+  }
+
+}
index 48e57e23f7671f0bf5025ef3b0777fce90d2c7a0..3e3514b115b14942374fef9e52084f75fbc9a7df 100644 (file)
@@ -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 {
 
index c471d2341fc4c407b2c1e45c9f24f075a2f0d69f..f2129085694c4090c878774dad16b38a9bcfa0c4 100644 (file)
@@ -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 {