From 154617a5034da3f35da19d415bc02939f395d25d Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 27 May 2015 09:44:56 +0200 Subject: [PATCH] SONAR-6589 Support both pre-order and post-order in typeAwareVisitor --- ...va => DepthTraversalTypeAwareVisitor.java} | 52 ++-- .../step/QualityProfileEventsStep.java | 16 +- ...derDepthTraversalTypeAwareVisitorTest.java | 266 ++++++++++++++++++ ...erDepthTraversalTypeAwareVisitorTest.java} | 29 +- 4 files changed, 327 insertions(+), 36 deletions(-) rename server/sonar-server/src/main/java/org/sonar/server/computation/component/{ChildFirstTypeAwareVisitor.java => DepthTraversalTypeAwareVisitor.java} (65%) create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/component/PostOrderDepthTraversalTypeAwareVisitorTest.java rename server/sonar-server/src/test/java/org/sonar/server/computation/component/{ChildFirstTypeAwareVisitorTest.java => PreOrderDepthTraversalTypeAwareVisitorTest.java} (91%) diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ChildFirstTypeAwareVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java similarity index 65% rename from server/sonar-server/src/main/java/org/sonar/server/computation/component/ChildFirstTypeAwareVisitor.java rename to server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java index 70e0d37a607..c368b15bccc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ChildFirstTypeAwareVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java @@ -17,20 +17,23 @@ * 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.Objects; +import static java.util.Objects.requireNonNull; /** - * Implementation of {@link TypeAwareVisitor} that implements a depth first crawling of the - * {@link ComponentImpl} tree. It supports a max depth for crawling (component strictly deeper than the specified - * type will be ignored). + * Implementation of {@link TypeAwareVisitor} that implements a depth traversal of a {@link Component} tree. + *

It supports visiting traversal in either pre-order or post-order

+ * It supports a max depth for crawling (component strictly deeper than the specified type will be ignored). */ -public abstract class ChildFirstTypeAwareVisitor implements TypeAwareVisitor { +public abstract class DepthTraversalTypeAwareVisitor implements TypeAwareVisitor { private final Component.Type maxDepth; + private final Order order; - protected ChildFirstTypeAwareVisitor(Component.Type maxDepth) { - this.maxDepth = Objects.requireNonNull(maxDepth); + protected DepthTraversalTypeAwareVisitor(Component.Type maxDepth, Order order) { + this.maxDepth = requireNonNull(maxDepth); + this.order = requireNonNull(order); } @Override @@ -39,12 +42,18 @@ public abstract class ChildFirstTypeAwareVisitor implements TypeAwareVisitor { return; } - if (component.getType() != maxDepth) { - for (Component child : component.getChildren()) { - visit(child); - } + if (order == Order.PRE_ORDER) { + visitNode(component); } + visitChildren(component); + + if (order == Order.POST_ORDER) { + visitNode(component); + } + } + + private void visitNode(Component component) { switch (component.getType()) { case PROJECT: visitProject(component); @@ -63,29 +72,40 @@ public abstract class ChildFirstTypeAwareVisitor implements TypeAwareVisitor { } } + private void visitChildren(Component component) { + if (component.getType() != maxDepth) { + for (Component child : component.getChildren()) { + visit(child); + } + } + } + @Override - public void visitProject(Component tree) { + public void visitProject(Component project) { // empty implementation, meant to be override at will by subclasses } @Override - public void visitModule(Component tree) { + public void visitModule(Component module) { // empty implementation, meant to be override at will by subclasses } @Override - public void visitDirectory(Component tree) { + public void visitDirectory(Component directory) { // empty implementation, meant to be override at will by subclasses } @Override - public void visitFile(Component tree) { + public void visitFile(Component file) { // empty implementation, meant to be override at will by subclasses } @Override - public void visitUnknown(Component tree) { + public void visitUnknown(Component unknownComponent) { // 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/step/QualityProfileEventsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java index 4bb1e5f019e..3846599a0f2 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 @@ -21,9 +21,6 @@ package org.sonar.server.computation.step; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSortedMap; -import java.util.Date; -import java.util.Map; -import javax.annotation.Nullable; import org.apache.commons.lang.time.DateUtils; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.resources.Language; @@ -32,17 +29,24 @@ import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.UtcDateUtils; import org.sonar.core.measure.db.MeasureDto; import org.sonar.server.computation.ComputationContext; -import org.sonar.server.computation.component.ChildFirstTypeAwareVisitor; import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.computation.event.Event; -import org.sonar.server.computation.qualityprofile.QualityProfile; import org.sonar.server.computation.qualityprofile.QPMeasureData; +import org.sonar.server.computation.qualityprofile.QualityProfile; + +import javax.annotation.Nullable; + +import java.util.Date; +import java.util.Map; + +import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.POST_ORDER; public class QualityProfileEventsStep implements ComputationStep { @Override public void execute(ComputationContext context) { - new ChildFirstTypeAwareVisitor(Component.Type.PROJECT) { + new DepthTraversalTypeAwareVisitor(Component.Type.PROJECT, POST_ORDER) { @Override public void visitProject(Component tree) { executeForProject(tree); 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 new file mode 100644 index 00000000000..50bd7394ea7 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/PostOrderDepthTraversalTypeAwareVisitorTest.java @@ -0,0 +1,266 @@ +/* + * 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 org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; +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; + +public class PostOrderDepthTraversalTypeAwareVisitorTest { + + private static final Component FILE_4 = component(FILE, 4); + private static final Component FILE_5 = component(FILE, 5); + private static final Component DIRECTORY_3 = component(DIRECTORY, 3, FILE_4, FILE_5); + private static final Component MODULE_2 = component(MODULE, 2, DIRECTORY_3); + private static final Component COMPONENT_TREE = component(PROJECT, 1, MODULE_2); + + private final DepthTraversalTypeAwareVisitor spyProjectVisitor = spy(new DepthTraversalTypeAwareVisitor(PROJECT, POST_ORDER) {}); + private final DepthTraversalTypeAwareVisitor spyModuleVisitor = spy(new DepthTraversalTypeAwareVisitor(MODULE, POST_ORDER) {}); + private final DepthTraversalTypeAwareVisitor spyDirectoryVisitor = spy(new DepthTraversalTypeAwareVisitor(DIRECTORY, POST_ORDER) {}); + private final DepthTraversalTypeAwareVisitor spyFileVisitor = spy(new DepthTraversalTypeAwareVisitor(FILE, POST_ORDER) {}); + private final InOrder inOrder = inOrder(spyProjectVisitor, spyModuleVisitor, spyDirectoryVisitor, spyFileVisitor); + + @Test(expected = NullPointerException.class) + public void non_null_max_depth_fast_fail() { + new DepthTraversalTypeAwareVisitor(null, POST_ORDER) {}; + } + + @Test(expected = NullPointerException.class) + public void visit_null_Component_throws_NPE() { + spyFileVisitor.visit(null); + } + + @Test + public void visit_file_with_depth_FILE_calls_visit_file() { + Component component = component(FILE, 1); + spyFileVisitor.visit(component); + + inOrder.verify(spyFileVisitor).visit(component); + inOrder.verify(spyFileVisitor).visitFile(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_module_with_depth_FILE_calls_visit_module() { + Component component = component(MODULE, 1); + spyFileVisitor.visit(component); + + inOrder.verify(spyFileVisitor).visit(component); + inOrder.verify(spyFileVisitor).visitModule(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_directory_with_depth_FILE_calls_visit_directory() { + Component component = component(DIRECTORY, 1); + spyFileVisitor.visit(component); + + inOrder.verify(spyFileVisitor).visit(component); + inOrder.verify(spyFileVisitor).visitDirectory(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_project_with_depth_FILE_calls_visit_project() { + Component component = component(PROJECT, 1); + spyFileVisitor.visit(component); + + inOrder.verify(spyFileVisitor).visit(component); + inOrder.verify(spyFileVisitor).visitProject(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_file_with_depth_DIRECTORY_does_not_call_visit_file() { + Component component = component(FILE, 1); + spyDirectoryVisitor.visit(component); + + inOrder.verify(spyDirectoryVisitor).visit(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_directory_with_depth_DIRECTORY_calls_visit_directory() { + Component component = component(DIRECTORY, 1); + spyDirectoryVisitor.visit(component); + + inOrder.verify(spyDirectoryVisitor).visit(component); + inOrder.verify(spyDirectoryVisitor).visitDirectory(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_module_with_depth_DIRECTORY_calls_visit_module() { + Component component = component(MODULE, 1); + spyDirectoryVisitor.visit(component); + + inOrder.verify(spyDirectoryVisitor).visit(component); + inOrder.verify(spyDirectoryVisitor).visitModule(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_project_with_depth_DIRECTORY_calls_visit_project() { + Component component = component(PROJECT, 1); + spyDirectoryVisitor.visit(component); + + inOrder.verify(spyDirectoryVisitor).visit(component); + inOrder.verify(spyDirectoryVisitor).visitProject(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_file_with_depth_MODULE_does_not_call_visit_file() { + Component component = component(FILE, 1); + spyModuleVisitor.visit(component); + + inOrder.verify(spyModuleVisitor).visit(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_directory_with_depth_MODULE_does_not_call_visit_directory() { + Component component = component(DIRECTORY, 1); + spyModuleVisitor.visit(component); + + inOrder.verify(spyModuleVisitor).visit(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_module_with_depth_MODULE_calls_visit_module() { + Component component = component(MODULE, 1); + spyModuleVisitor.visit(component); + + inOrder.verify(spyModuleVisitor).visit(component); + inOrder.verify(spyModuleVisitor).visitModule(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_project_with_depth_MODULE_calls_visit_project() { + Component component = component(MODULE, 1); + spyModuleVisitor.visit(component); + + inOrder.verify(spyModuleVisitor).visit(component); + inOrder.verify(spyModuleVisitor).visitModule(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_file_with_depth_PROJECT_does_not_call_visit_file() { + Component component = component(FILE, 1); + spyProjectVisitor.visit(component); + + inOrder.verify(spyProjectVisitor).visit(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_directory_with_depth_PROJECT_does_not_call_visit_directory() { + Component component = component(DIRECTORY, 1); + spyProjectVisitor.visit(component); + + inOrder.verify(spyProjectVisitor).visit(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_module_with_depth_PROJECT_does_not_call_visit_module() { + Component component = component(MODULE, 1); + spyProjectVisitor.visit(component); + + inOrder.verify(spyProjectVisitor).visit(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void visit_project_with_depth_PROJECT_calls_visit_project() { + Component component = component(PROJECT, 1); + spyProjectVisitor.visit(component); + + inOrder.verify(spyProjectVisitor).visit(component); + inOrder.verify(spyProjectVisitor).visitProject(component); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void verify_visit_call_when_visit_tree_with_depth_FILE() { + spyFileVisitor.visit(COMPONENT_TREE); + + inOrder.verify(spyFileVisitor).visit(COMPONENT_TREE); + inOrder.verify(spyFileVisitor).visit(MODULE_2); + inOrder.verify(spyFileVisitor).visit(DIRECTORY_3); + inOrder.verify(spyFileVisitor).visit(FILE_4); + inOrder.verify(spyFileVisitor).visitFile(FILE_4); + inOrder.verify(spyFileVisitor).visit(FILE_5); + inOrder.verify(spyFileVisitor).visitFile(FILE_5); + inOrder.verify(spyFileVisitor).visitDirectory(DIRECTORY_3); + inOrder.verify(spyFileVisitor).visitModule(MODULE_2); + inOrder.verify(spyFileVisitor).visitProject(COMPONENT_TREE); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void verify_visit_call_when_visit_tree_with_depth_DIRECTORY() { + spyDirectoryVisitor.visit(COMPONENT_TREE); + + inOrder.verify(spyDirectoryVisitor).visit(COMPONENT_TREE); + inOrder.verify(spyDirectoryVisitor).visit(MODULE_2); + inOrder.verify(spyDirectoryVisitor).visit(DIRECTORY_3); + inOrder.verify(spyDirectoryVisitor).visitDirectory(DIRECTORY_3); + inOrder.verify(spyDirectoryVisitor).visitModule(MODULE_2); + inOrder.verify(spyDirectoryVisitor).visitProject(COMPONENT_TREE); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void verify_visit_call_when_visit_tree_with_depth_MODULE() { + spyModuleVisitor.visit(COMPONENT_TREE); + + inOrder.verify(spyModuleVisitor).visit(COMPONENT_TREE); + inOrder.verify(spyModuleVisitor).visit(MODULE_2); + inOrder.verify(spyModuleVisitor).visitModule(MODULE_2); + inOrder.verify(spyModuleVisitor).visitProject(COMPONENT_TREE); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void verify_visit_call_when_visit_tree_with_depth_PROJECT() { + spyProjectVisitor.visit(COMPONENT_TREE); + + inOrder.verify(spyProjectVisitor).visit(COMPONENT_TREE); + inOrder.verify(spyProjectVisitor).visitProject(COMPONENT_TREE); + inOrder.verifyNoMoreInteractions(); + } + + private static Component component(final Component.Type type, final int ref, final Component... children) { + return new DumbComponent(type, ref, null, null, children); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/ChildFirstTypeAwareVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/PreOrderDepthTraversalTypeAwareVisitorTest.java similarity index 91% rename from server/sonar-server/src/test/java/org/sonar/server/computation/component/ChildFirstTypeAwareVisitorTest.java rename to server/sonar-server/src/test/java/org/sonar/server/computation/component/PreOrderDepthTraversalTypeAwareVisitorTest.java index 6f763397ad6..fffa395d5eb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/ChildFirstTypeAwareVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/PreOrderDepthTraversalTypeAwareVisitorTest.java @@ -17,6 +17,7 @@ * 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 org.junit.Test; @@ -28,8 +29,9 @@ 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; -public class ChildFirstTypeAwareVisitorTest { +public class PreOrderDepthTraversalTypeAwareVisitorTest { private static final Component FILE_4 = component(FILE, 4); private static final Component FILE_5 = component(FILE, 5); @@ -37,16 +39,15 @@ public class ChildFirstTypeAwareVisitorTest { private static final Component MODULE_2 = component(MODULE, 2, DIRECTORY_3); private static final Component COMPONENT_TREE = component(PROJECT, 1, MODULE_2); - private final ChildFirstTypeAwareVisitor spyProjectVisitor = spy(new ChildFirstTypeAwareVisitor(PROJECT) {}); - private final ChildFirstTypeAwareVisitor spyModuleVisitor = spy(new ChildFirstTypeAwareVisitor(MODULE) {}); - private final ChildFirstTypeAwareVisitor spyDirectoryVisitor = spy(new ChildFirstTypeAwareVisitor(DIRECTORY) {}); - private final ChildFirstTypeAwareVisitor spyFileVisitor = spy(new ChildFirstTypeAwareVisitor(FILE) {}); + private final DepthTraversalTypeAwareVisitor spyProjectVisitor = spy(new DepthTraversalTypeAwareVisitor(PROJECT, PRE_ORDER) {}); + private final DepthTraversalTypeAwareVisitor spyModuleVisitor = spy(new DepthTraversalTypeAwareVisitor(MODULE, PRE_ORDER) {}); + private final DepthTraversalTypeAwareVisitor spyDirectoryVisitor = spy(new DepthTraversalTypeAwareVisitor(DIRECTORY, PRE_ORDER) {}); + private final DepthTraversalTypeAwareVisitor spyFileVisitor = spy(new DepthTraversalTypeAwareVisitor(FILE, PRE_ORDER) {}); private final InOrder inOrder = inOrder(spyProjectVisitor, spyModuleVisitor, spyDirectoryVisitor, spyFileVisitor); @Test(expected = NullPointerException.class) public void non_null_max_depth_fast_fail() { - new ChildFirstTypeAwareVisitor(null) { - }; + new DepthTraversalTypeAwareVisitor(null, PRE_ORDER) {}; } @Test(expected = NullPointerException.class) @@ -213,15 +214,15 @@ public class ChildFirstTypeAwareVisitorTest { spyFileVisitor.visit(COMPONENT_TREE); inOrder.verify(spyFileVisitor).visit(COMPONENT_TREE); + inOrder.verify(spyFileVisitor).visitProject(COMPONENT_TREE); inOrder.verify(spyFileVisitor).visit(MODULE_2); + inOrder.verify(spyFileVisitor).visitModule(MODULE_2); inOrder.verify(spyFileVisitor).visit(DIRECTORY_3); + inOrder.verify(spyFileVisitor).visitDirectory(DIRECTORY_3); inOrder.verify(spyFileVisitor).visit(FILE_4); inOrder.verify(spyFileVisitor).visitFile(FILE_4); inOrder.verify(spyFileVisitor).visit(FILE_5); inOrder.verify(spyFileVisitor).visitFile(FILE_5); - inOrder.verify(spyFileVisitor).visitDirectory(DIRECTORY_3); - inOrder.verify(spyFileVisitor).visitModule(MODULE_2); - inOrder.verify(spyFileVisitor).visitProject(COMPONENT_TREE); inOrder.verifyNoMoreInteractions(); } @@ -230,11 +231,11 @@ public class ChildFirstTypeAwareVisitorTest { spyDirectoryVisitor.visit(COMPONENT_TREE); inOrder.verify(spyDirectoryVisitor).visit(COMPONENT_TREE); + inOrder.verify(spyDirectoryVisitor).visitProject(COMPONENT_TREE); inOrder.verify(spyDirectoryVisitor).visit(MODULE_2); + inOrder.verify(spyDirectoryVisitor).visitModule(MODULE_2); inOrder.verify(spyDirectoryVisitor).visit(DIRECTORY_3); inOrder.verify(spyDirectoryVisitor).visitDirectory(DIRECTORY_3); - inOrder.verify(spyDirectoryVisitor).visitModule(MODULE_2); - inOrder.verify(spyDirectoryVisitor).visitProject(COMPONENT_TREE); inOrder.verifyNoMoreInteractions(); } @@ -243,9 +244,9 @@ public class ChildFirstTypeAwareVisitorTest { spyModuleVisitor.visit(COMPONENT_TREE); inOrder.verify(spyModuleVisitor).visit(COMPONENT_TREE); + inOrder.verify(spyModuleVisitor).visitProject(COMPONENT_TREE); inOrder.verify(spyModuleVisitor).visit(MODULE_2); inOrder.verify(spyModuleVisitor).visitModule(MODULE_2); - inOrder.verify(spyModuleVisitor).visitProject(COMPONENT_TREE); inOrder.verifyNoMoreInteractions(); } @@ -259,7 +260,7 @@ public class ChildFirstTypeAwareVisitorTest { } private static Component component(final Component.Type type, final int ref, final Component... children) { - return new DumbComponent(type, ref, children); + return new DumbComponent(type, ref, null, null, children); } } -- 2.39.5