From 3ed43c3c3ded515b31927f1cb80b999c48dcf4bd Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 18 Sep 2025 12:34:19 +0200 Subject: [PATCH 1/5] Make levelOrder the default visitor Apply same strategy to classpath collection as we apply to dependency collection ("nearest wins"). --- .../org/eclipse/aether/ConfigurationProperties.java | 10 +++++++++- .../aether/internal/impl/DefaultRepositorySystem.java | 2 +- src/site/markdown/configuration.md | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java b/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java index 4804756a9..aa055f4f0 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java @@ -524,7 +524,7 @@ public final class ConfigurationProperties { * @since 2.0.0 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} * @configurationType {@link java.lang.String} - * @configurationDefaultValue {@link #REPOSITORY_SYSTEM_DEPENDENCY_VISITOR_PREORDER} + * @configurationDefaultValue {@link #DEFAULT_REPOSITORY_SYSTEM_DEPENDENCY_VISITOR} * @configurationRepoIdSuffix No */ public static final String REPOSITORY_SYSTEM_DEPENDENCY_VISITOR = PREFIX_SYSTEM + "dependencyVisitor"; @@ -551,6 +551,14 @@ public final class ConfigurationProperties { */ public static final String REPOSITORY_SYSTEM_DEPENDENCY_VISITOR_LEVELORDER = "levelOrder"; + /** + * The default visitor strategy. + * + * @since 2.0.12 + */ + public static final String DEFAULT_REPOSITORY_SYSTEM_DEPENDENCY_VISITOR = + REPOSITORY_SYSTEM_DEPENDENCY_VISITOR_LEVELORDER; + /** * A flag indicating whether version scheme cache statistics should be printed on JVM shutdown. * This is useful for analyzing cache performance and effectiveness in development and testing scenarios. diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java index 947e079d9..6b68a9cac 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java @@ -340,7 +340,7 @@ private DependencyVisitor getDependencyVisitor( RepositorySystemSession session, Consumer nodeConsumer) { String strategy = ConfigUtils.getString( session, - ConfigurationProperties.REPOSITORY_SYSTEM_DEPENDENCY_VISITOR_PREORDER, + ConfigurationProperties.DEFAULT_REPOSITORY_SYSTEM_DEPENDENCY_VISITOR, ConfigurationProperties.REPOSITORY_SYSTEM_DEPENDENCY_VISITOR); switch (strategy) { case PreorderDependencyNodeConsumerVisitor.NAME: diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index 2ef1f2e5a..ec6426a47 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -116,7 +116,7 @@ To modify this file, edit the template and regenerate. | `"aether.syncContext.named.retry.wait"` | `Long` | The amount of milliseconds to wait between retries on time-out. | `200l` | 1.7.0 | No | Session Configuration | | `"aether.syncContext.named.time"` | `Long` | The maximum of time amount to be blocked to obtain lock. | `30l` | 1.7.0 | No | Session Configuration | | `"aether.syncContext.named.time.unit"` | `String` | The unit of maximum time amount to be blocked to obtain lock. Use TimeUnit enum names. | `"SECONDS"` | 1.7.0 | No | Session Configuration | -| `"aether.system.dependencyVisitor"` | `String` | A flag indicating which visitor should be used to "flatten" the dependency graph into list. Default is same as in older resolver versions "preOrder", while it can accept values like "postOrder" and "levelOrder". | `"preOrder"` | 2.0.0 | No | Session Configuration | +| `"aether.system.dependencyVisitor"` | `String` | A flag indicating which visitor should be used to "flatten" the dependency graph into list. Default is same as in older resolver versions "preOrder", while it can accept values like "postOrder" and "levelOrder". | `"levelOrder"` | 2.0.0 | No | Session Configuration | | `"aether.transport.apache.followRedirects"` | `Boolean` | If enabled, Apache HttpClient will follow HTTP redirects. | `true` | 2.0.2 | Yes | Session Configuration | | `"aether.transport.apache.https.cipherSuites"` | `String` | Comma-separated list of Cipher Suites which are enabled for HTTPS connections. | - | 2.0.0 | No | Session Configuration | | `"aether.transport.apache.https.protocols"` | `String` | Comma-separated list of Protocols which are enabled for HTTPS connections. | - | 2.0.0 | No | Session Configuration | From 03d69792d8382bd0917e9339e03f8f469312439c Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 18 Sep 2025 12:38:02 +0200 Subject: [PATCH 2/5] Update Javadoc --- .../java/org/eclipse/aether/ConfigurationProperties.java | 5 +++-- src/site/markdown/configuration.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java b/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java index aa055f4f0..61dc377fa 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java @@ -515,8 +515,9 @@ public final class ConfigurationProperties { public static final String HTTPS_SECURITY_MODE_INSECURE = "insecure"; /** - * A flag indicating which visitor should be used to "flatten" the dependency graph into list. Default is - * same as in older resolver versions "preOrder", while it can accept values like "postOrder" and "levelOrder". + * A flag indicating which visitor should be used to "flatten" the dependency graph into list. In Resolver 2 + * the default is new "levelOrder", while Maven 3 used "preOrder". This property accepts values + * "preOrder", "postOrder" and "levelOrder". * * @see #REPOSITORY_SYSTEM_DEPENDENCY_VISITOR_PREORDER * @see #REPOSITORY_SYSTEM_DEPENDENCY_VISITOR_POSTORDER diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index ec6426a47..e2d89a2b4 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -116,7 +116,7 @@ To modify this file, edit the template and regenerate. | `"aether.syncContext.named.retry.wait"` | `Long` | The amount of milliseconds to wait between retries on time-out. | `200l` | 1.7.0 | No | Session Configuration | | `"aether.syncContext.named.time"` | `Long` | The maximum of time amount to be blocked to obtain lock. | `30l` | 1.7.0 | No | Session Configuration | | `"aether.syncContext.named.time.unit"` | `String` | The unit of maximum time amount to be blocked to obtain lock. Use TimeUnit enum names. | `"SECONDS"` | 1.7.0 | No | Session Configuration | -| `"aether.system.dependencyVisitor"` | `String` | A flag indicating which visitor should be used to "flatten" the dependency graph into list. Default is same as in older resolver versions "preOrder", while it can accept values like "postOrder" and "levelOrder". | `"levelOrder"` | 2.0.0 | No | Session Configuration | +| `"aether.system.dependencyVisitor"` | `String` | A flag indicating which visitor should be used to "flatten" the dependency graph into list. In Resolver 2 the default is new "levelOrder", while Maven 3 used "preOrder". This property accepts values "preOrder", "postOrder" and "levelOrder". | `"levelOrder"` | 2.0.0 | No | Session Configuration | | `"aether.transport.apache.followRedirects"` | `Boolean` | If enabled, Apache HttpClient will follow HTTP redirects. | `true` | 2.0.2 | Yes | Session Configuration | | `"aether.transport.apache.https.cipherSuites"` | `String` | Comma-separated list of Cipher Suites which are enabled for HTTPS connections. | - | 2.0.0 | No | Session Configuration | | `"aether.transport.apache.https.protocols"` | `String` | Comma-separated list of Protocols which are enabled for HTTPS connections. | - | 2.0.0 | No | Session Configuration | From e50b0274bf9cb456eaf1c5af3d8c22e1434b9a01 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 19 Sep 2025 15:13:52 +0200 Subject: [PATCH 3/5] Update maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java Co-authored-by: Guillaume Nodet --- .../main/java/org/eclipse/aether/ConfigurationProperties.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java b/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java index 61dc377fa..327815fdb 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java @@ -515,7 +515,7 @@ public final class ConfigurationProperties { public static final String HTTPS_SECURITY_MODE_INSECURE = "insecure"; /** - * A flag indicating which visitor should be used to "flatten" the dependency graph into list. In Resolver 2 + * A flag indicating which visitor should be used to "flatten" the dependency graph into list. In Maven 4 * the default is new "levelOrder", while Maven 3 used "preOrder". This property accepts values * "preOrder", "postOrder" and "levelOrder". * From 2bcbe74d3feffc0af184ebac23f102bf176e9342 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 19 Sep 2025 16:16:38 +0200 Subject: [PATCH 4/5] Fixing levelOrder and all other node consumer visitor. They should NOT be embedded into viltering visitor ever (level order loses context, other two are not really affected) instead they got support for filtering. --- .../impl/DefaultRepositorySystem.java | 14 +++----- ...AbstractDependencyNodeConsumerVisitor.java | 36 ++++++++++++++++--- ...velOrderDependencyNodeConsumerVisitor.java | 21 ++++++++--- ...ostorderDependencyNodeConsumerVisitor.java | 21 ++++++++--- ...PreorderDependencyNodeConsumerVisitor.java | 21 ++++++++--- .../graph/visitor/NodeListGeneratorTest.java | 36 +++++++++++++++++++ src/site/markdown/configuration.md | 2 +- 7 files changed, 125 insertions(+), 26 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java index 6b68a9cac..90003a7c2 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java @@ -92,7 +92,6 @@ import org.eclipse.aether.spi.artifact.decorator.ArtifactDecoratorFactory; import org.eclipse.aether.spi.synccontext.SyncContextFactory; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor; import org.eclipse.aether.util.graph.visitor.LevelOrderDependencyNodeConsumerVisitor; import org.eclipse.aether.util.graph.visitor.PostorderDependencyNodeConsumerVisitor; import org.eclipse.aether.util.graph.visitor.PreorderDependencyNodeConsumerVisitor; @@ -328,27 +327,24 @@ private List doFlattenDependencyNodes( RepositorySystemSession session, DependencyNode root, DependencyFilter dependencyFilter) { final ArrayList dependencyNodes = new ArrayList<>(); if (root != null) { - DependencyVisitor builder = getDependencyVisitor(session, dependencyNodes::add); - DependencyVisitor visitor = - (dependencyFilter != null) ? new FilteringDependencyVisitor(builder, dependencyFilter) : builder; - root.accept(visitor); + root.accept(getDependencyVisitor(session, dependencyNodes::add, dependencyFilter)); } return dependencyNodes; } private DependencyVisitor getDependencyVisitor( - RepositorySystemSession session, Consumer nodeConsumer) { + RepositorySystemSession session, Consumer nodeConsumer, DependencyFilter dependencyFilter) { String strategy = ConfigUtils.getString( session, ConfigurationProperties.DEFAULT_REPOSITORY_SYSTEM_DEPENDENCY_VISITOR, ConfigurationProperties.REPOSITORY_SYSTEM_DEPENDENCY_VISITOR); switch (strategy) { case PreorderDependencyNodeConsumerVisitor.NAME: - return new PreorderDependencyNodeConsumerVisitor(nodeConsumer); + return new PreorderDependencyNodeConsumerVisitor(nodeConsumer, dependencyFilter); case PostorderDependencyNodeConsumerVisitor.NAME: - return new PostorderDependencyNodeConsumerVisitor(nodeConsumer); + return new PostorderDependencyNodeConsumerVisitor(nodeConsumer, dependencyFilter); case LevelOrderDependencyNodeConsumerVisitor.NAME: - return new LevelOrderDependencyNodeConsumerVisitor(nodeConsumer); + return new LevelOrderDependencyNodeConsumerVisitor(nodeConsumer, dependencyFilter); default: throw new IllegalArgumentException("Invalid dependency visitor strategy: " + strategy); } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/AbstractDependencyNodeConsumerVisitor.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/AbstractDependencyNodeConsumerVisitor.java index 75864c5b2..073178761 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/AbstractDependencyNodeConsumerVisitor.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/AbstractDependencyNodeConsumerVisitor.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.function.Consumer; +import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.graph.DependencyVisitor; @@ -29,16 +30,27 @@ /** * Abstract base class for dependency tree traverses that feed {@link Consumer}. + *

+ * Implementations derived from this class cannot be embedded into {@link FilteringDependencyVisitor}, + * this is why these classes accept {@link DependencyFilter} in constructor instead. * * @since 2.0.0 */ abstract class AbstractDependencyNodeConsumerVisitor implements DependencyVisitor { - protected final Consumer nodeConsumer; + private static final DependencyFilter ACCEPT_ALL = (d, p) -> true; + + private final Consumer nodeConsumer; + + private final DependencyFilter filter; + + private final Stack path; private final Map visitedNodes; - protected AbstractDependencyNodeConsumerVisitor(Consumer nodeConsumer) { + protected AbstractDependencyNodeConsumerVisitor(Consumer nodeConsumer, DependencyFilter filter) { this.nodeConsumer = requireNonNull(nodeConsumer); + this.filter = filter == null ? ACCEPT_ALL : filter; + this.path = new Stack<>(); this.visitedNodes = new IdentityHashMap<>(512); } @@ -53,8 +65,24 @@ protected boolean setVisited(DependencyNode node) { } @Override - public abstract boolean visitEnter(DependencyNode node); + public final boolean visitEnter(DependencyNode node) { + path.push(node); + return doVisitEnter(node); + } + + protected abstract boolean doVisitEnter(DependencyNode node); @Override - public abstract boolean visitLeave(DependencyNode node); + public final boolean visitLeave(DependencyNode node) { + path.pop(); + return doVisitLeave(node); + } + + protected abstract boolean doVisitLeave(DependencyNode node); + + protected void mayConsume(DependencyNode node) { + if (filter.accept(node, path)) { + nodeConsumer.accept(node); + } + } } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/LevelOrderDependencyNodeConsumerVisitor.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/LevelOrderDependencyNodeConsumerVisitor.java index 977cee497..0e08caa4a 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/LevelOrderDependencyNodeConsumerVisitor.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/LevelOrderDependencyNodeConsumerVisitor.java @@ -23,12 +23,16 @@ import java.util.function.Consumer; import org.eclipse.aether.ConfigurationProperties; +import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.graph.DependencyNode; /** * Processes dependency graph by traversing the graph in level order. This visitor visits each node exactly once * regardless how many paths within the dependency graph lead to the node such that the resulting node sequence is * free of duplicates. + *

+ * Instances of this class cannot be embedded into {@link FilteringDependencyVisitor}, pass in the + * filter {@link DependencyFilter} into constructor instead. * * @see NodeListGenerator * @since 2.0.0 @@ -45,13 +49,22 @@ public final class LevelOrderDependencyNodeConsumerVisitor extends AbstractDepen * Creates a new level order list generator. */ public LevelOrderDependencyNodeConsumerVisitor(Consumer nodeConsumer) { - super(nodeConsumer); + this(nodeConsumer, null); + } + + /** + * Creates a new level order list generator with filter. + * + * @since 2.0.12 + */ + public LevelOrderDependencyNodeConsumerVisitor(Consumer nodeConsumer, DependencyFilter filter) { + super(nodeConsumer, filter); nodesPerLevel = new HashMap<>(16); visits = new Stack<>(); } @Override - public boolean visitEnter(DependencyNode node) { + protected boolean doVisitEnter(DependencyNode node) { boolean visited = !setVisited(node); visits.push(visited); if (!visited) { @@ -61,14 +74,14 @@ public boolean visitEnter(DependencyNode node) { } @Override - public boolean visitLeave(DependencyNode node) { + protected boolean doVisitLeave(DependencyNode node) { Boolean visited = visits.pop(); if (visited) { return true; } if (visits.isEmpty()) { for (int l = 1; nodesPerLevel.containsKey(l); l++) { - nodesPerLevel.get(l).forEach(nodeConsumer); + nodesPerLevel.get(l).forEach(this::mayConsume); } } return true; diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PostorderDependencyNodeConsumerVisitor.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PostorderDependencyNodeConsumerVisitor.java index ae5c813f6..e7dd32f57 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PostorderDependencyNodeConsumerVisitor.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PostorderDependencyNodeConsumerVisitor.java @@ -21,12 +21,16 @@ import java.util.function.Consumer; import org.eclipse.aether.ConfigurationProperties; +import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.graph.DependencyNode; /** * Processes dependency graph by traversing the graph in postorder. This visitor visits each node exactly once * regardless how many paths within the dependency graph lead to the node such that the resulting node sequence is * free of duplicates. + *

+ * Instances of this class cannot be embedded into {@link FilteringDependencyVisitor}, pass in the + * filter {@link DependencyFilter} into constructor instead. * * @see NodeListGenerator * @since 2.0.0 @@ -41,24 +45,33 @@ public final class PostorderDependencyNodeConsumerVisitor extends AbstractDepend * Creates a new postorder list generator. */ public PostorderDependencyNodeConsumerVisitor(Consumer nodeConsumer) { - super(nodeConsumer); + this(nodeConsumer, null); + } + + /** + * Creates a new postorder list generator. + * + * @since 2.0.12 + */ + public PostorderDependencyNodeConsumerVisitor(Consumer nodeConsumer, DependencyFilter filter) { + super(nodeConsumer, filter); visits = new Stack<>(); } @Override - public boolean visitEnter(DependencyNode node) { + protected boolean doVisitEnter(DependencyNode node) { boolean visited = !setVisited(node); visits.push(visited); return !visited; } @Override - public boolean visitLeave(DependencyNode node) { + protected boolean doVisitLeave(DependencyNode node) { Boolean visited = visits.pop(); if (visited) { return true; } - nodeConsumer.accept(node); + mayConsume(node); return true; } } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PreorderDependencyNodeConsumerVisitor.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PreorderDependencyNodeConsumerVisitor.java index 86f934be4..84dbc67b6 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PreorderDependencyNodeConsumerVisitor.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PreorderDependencyNodeConsumerVisitor.java @@ -21,12 +21,16 @@ import java.util.function.Consumer; import org.eclipse.aether.ConfigurationProperties; +import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.graph.DependencyNode; /** * Processes dependency graph by traversing the graph in preorder. This visitor visits each node exactly once * regardless how many paths within the dependency graph lead to the node such that the resulting node sequence is * free of duplicates. + *

+ * Instances of this class cannot be embedded into {@link FilteringDependencyVisitor}, pass in the + * filter {@link DependencyFilter} into constructor instead. * * @see NodeListGenerator * @since 2.0.0 @@ -39,20 +43,29 @@ public final class PreorderDependencyNodeConsumerVisitor extends AbstractDepende * Creates a new preorder list generator. */ public PreorderDependencyNodeConsumerVisitor(Consumer nodeConsumer) { - super(nodeConsumer); + this(nodeConsumer, null); + } + + /** + * Creates a new preorder list generator. + * + * @since 2.0.12 + */ + public PreorderDependencyNodeConsumerVisitor(Consumer nodeConsumer, DependencyFilter filter) { + super(nodeConsumer, filter); } @Override - public boolean visitEnter(DependencyNode node) { + protected boolean doVisitEnter(DependencyNode node) { if (!setVisited(node)) { return false; } - nodeConsumer.accept(node); + mayConsume(node); return true; } @Override - public boolean visitLeave(DependencyNode node) { + protected boolean doVisitLeave(DependencyNode node) { return true; } } diff --git a/maven-resolver-util/src/test/java/org/eclipse/aether/util/graph/visitor/NodeListGeneratorTest.java b/maven-resolver-util/src/test/java/org/eclipse/aether/util/graph/visitor/NodeListGeneratorTest.java index e8f59fec0..a65b9bf03 100644 --- a/maven-resolver-util/src/test/java/org/eclipse/aether/util/graph/visitor/NodeListGeneratorTest.java +++ b/maven-resolver-util/src/test/java/org/eclipse/aether/util/graph/visitor/NodeListGeneratorTest.java @@ -218,4 +218,40 @@ public boolean visitLeave(DependencyNode node) { assertEquals(3, nodeListGenerator.getFiles().size()); assertEquals(fileNames, classPathNames); } + + @Test + void testPreOrderDuplicateSuppressionWithFilteringSimple() throws Exception { + DependencyNode root = parse("simple.txt"); + + NodeListGenerator nodeListGenerator = new NodeListGenerator(); + PreorderDependencyNodeConsumerVisitor visitor = new PreorderDependencyNodeConsumerVisitor( + nodeListGenerator, (d, p) -> !"a".equals(d.getArtifact().getArtifactId())); + root.accept(visitor); + + assertSequence(nodeListGenerator.getNodes(), "b", "c", "d", "e"); + } + + @Test + void testPostOrderDuplicateSuppressionWithFilteringSimple() throws Exception { + DependencyNode root = parse("simple.txt"); + + NodeListGenerator nodeListGenerator = new NodeListGenerator(); + PostorderDependencyNodeConsumerVisitor visitor = new PostorderDependencyNodeConsumerVisitor( + nodeListGenerator, (d, p) -> !"a".equals(d.getArtifact().getArtifactId())); + root.accept(visitor); + + assertSequence(nodeListGenerator.getNodes(), "c", "b", "e", "d"); + } + + @Test + void testLevelOrderDuplicateSuppressionWithFilteringSimple() throws Exception { + DependencyNode root = parse("simple.txt"); + + NodeListGenerator nodeListGenerator = new NodeListGenerator(); + LevelOrderDependencyNodeConsumerVisitor visitor = new LevelOrderDependencyNodeConsumerVisitor( + nodeListGenerator, (d, p) -> !"a".equals(d.getArtifact().getArtifactId())); + root.accept(visitor); + + assertSequence(nodeListGenerator.getNodes(), "b", "d", "c", "e"); + } } diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index e2d89a2b4..e30b9316e 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -116,7 +116,7 @@ To modify this file, edit the template and regenerate. | `"aether.syncContext.named.retry.wait"` | `Long` | The amount of milliseconds to wait between retries on time-out. | `200l` | 1.7.0 | No | Session Configuration | | `"aether.syncContext.named.time"` | `Long` | The maximum of time amount to be blocked to obtain lock. | `30l` | 1.7.0 | No | Session Configuration | | `"aether.syncContext.named.time.unit"` | `String` | The unit of maximum time amount to be blocked to obtain lock. Use TimeUnit enum names. | `"SECONDS"` | 1.7.0 | No | Session Configuration | -| `"aether.system.dependencyVisitor"` | `String` | A flag indicating which visitor should be used to "flatten" the dependency graph into list. In Resolver 2 the default is new "levelOrder", while Maven 3 used "preOrder". This property accepts values "preOrder", "postOrder" and "levelOrder". | `"levelOrder"` | 2.0.0 | No | Session Configuration | +| `"aether.system.dependencyVisitor"` | `String` | A flag indicating which visitor should be used to "flatten" the dependency graph into list. In Maven 4 the default is new "levelOrder", while Maven 3 used "preOrder". This property accepts values "preOrder", "postOrder" and "levelOrder". | `"levelOrder"` | 2.0.0 | No | Session Configuration | | `"aether.transport.apache.followRedirects"` | `Boolean` | If enabled, Apache HttpClient will follow HTTP redirects. | `true` | 2.0.2 | Yes | Session Configuration | | `"aether.transport.apache.https.cipherSuites"` | `String` | Comma-separated list of Cipher Suites which are enabled for HTTPS connections. | - | 2.0.0 | No | Session Configuration | | `"aether.transport.apache.https.protocols"` | `String` | Comma-separated list of Protocols which are enabled for HTTPS connections. | - | 2.0.0 | No | Session Configuration | From 984ec8f481b514c03fd93a4cf975e52a378e9aec Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 19 Sep 2025 16:28:34 +0200 Subject: [PATCH 5/5] Split methods --- .../visitor/AbstractDependencyNodeConsumerVisitor.java | 10 ++++++---- .../LevelOrderDependencyNodeConsumerVisitor.java | 8 ++++++-- .../PostorderDependencyNodeConsumerVisitor.java | 4 +++- .../visitor/PreorderDependencyNodeConsumerVisitor.java | 4 +++- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/AbstractDependencyNodeConsumerVisitor.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/AbstractDependencyNodeConsumerVisitor.java index 073178761..e40d89684 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/AbstractDependencyNodeConsumerVisitor.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/AbstractDependencyNodeConsumerVisitor.java @@ -80,9 +80,11 @@ public final boolean visitLeave(DependencyNode node) { protected abstract boolean doVisitLeave(DependencyNode node); - protected void mayConsume(DependencyNode node) { - if (filter.accept(node, path)) { - nodeConsumer.accept(node); - } + protected boolean acceptNode(DependencyNode node) { + return filter.accept(node, path); + } + + protected void consumeNode(DependencyNode node) { + nodeConsumer.accept(node); } } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/LevelOrderDependencyNodeConsumerVisitor.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/LevelOrderDependencyNodeConsumerVisitor.java index 0e08caa4a..074b81897 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/LevelOrderDependencyNodeConsumerVisitor.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/LevelOrderDependencyNodeConsumerVisitor.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.function.Consumer; import org.eclipse.aether.ConfigurationProperties; @@ -68,7 +69,10 @@ protected boolean doVisitEnter(DependencyNode node) { boolean visited = !setVisited(node); visits.push(visited); if (!visited) { - nodesPerLevel.computeIfAbsent(visits.size(), k -> new ArrayList<>()).add(node); + List nodesOnLevel = nodesPerLevel.computeIfAbsent(visits.size(), k -> new ArrayList<>()); + if (acceptNode(node)) { + nodesOnLevel.add(node); + } } return !visited; } @@ -81,7 +85,7 @@ protected boolean doVisitLeave(DependencyNode node) { } if (visits.isEmpty()) { for (int l = 1; nodesPerLevel.containsKey(l); l++) { - nodesPerLevel.get(l).forEach(this::mayConsume); + nodesPerLevel.get(l).forEach(this::consumeNode); } } return true; diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PostorderDependencyNodeConsumerVisitor.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PostorderDependencyNodeConsumerVisitor.java index e7dd32f57..a21c87c4b 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PostorderDependencyNodeConsumerVisitor.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PostorderDependencyNodeConsumerVisitor.java @@ -71,7 +71,9 @@ protected boolean doVisitLeave(DependencyNode node) { if (visited) { return true; } - mayConsume(node); + if (acceptNode(node)) { + consumeNode(node); + } return true; } } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PreorderDependencyNodeConsumerVisitor.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PreorderDependencyNodeConsumerVisitor.java index 84dbc67b6..1afa06206 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PreorderDependencyNodeConsumerVisitor.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PreorderDependencyNodeConsumerVisitor.java @@ -60,7 +60,9 @@ protected boolean doVisitEnter(DependencyNode node) { if (!setVisited(node)) { return false; } - mayConsume(node); + if (acceptNode(node)) { + consumeNode(node); + } return true; }