Skip to content

Commit 2bcbe74

Browse files
committed
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.
1 parent e50b027 commit 2bcbe74

File tree

7 files changed

+125
-26
lines changed

7 files changed

+125
-26
lines changed

maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@
9292
import org.eclipse.aether.spi.artifact.decorator.ArtifactDecoratorFactory;
9393
import org.eclipse.aether.spi.synccontext.SyncContextFactory;
9494
import org.eclipse.aether.util.ConfigUtils;
95-
import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor;
9695
import org.eclipse.aether.util.graph.visitor.LevelOrderDependencyNodeConsumerVisitor;
9796
import org.eclipse.aether.util.graph.visitor.PostorderDependencyNodeConsumerVisitor;
9897
import org.eclipse.aether.util.graph.visitor.PreorderDependencyNodeConsumerVisitor;
@@ -328,27 +327,24 @@ private List<DependencyNode> doFlattenDependencyNodes(
328327
RepositorySystemSession session, DependencyNode root, DependencyFilter dependencyFilter) {
329328
final ArrayList<DependencyNode> dependencyNodes = new ArrayList<>();
330329
if (root != null) {
331-
DependencyVisitor builder = getDependencyVisitor(session, dependencyNodes::add);
332-
DependencyVisitor visitor =
333-
(dependencyFilter != null) ? new FilteringDependencyVisitor(builder, dependencyFilter) : builder;
334-
root.accept(visitor);
330+
root.accept(getDependencyVisitor(session, dependencyNodes::add, dependencyFilter));
335331
}
336332
return dependencyNodes;
337333
}
338334

339335
private DependencyVisitor getDependencyVisitor(
340-
RepositorySystemSession session, Consumer<DependencyNode> nodeConsumer) {
336+
RepositorySystemSession session, Consumer<DependencyNode> nodeConsumer, DependencyFilter dependencyFilter) {
341337
String strategy = ConfigUtils.getString(
342338
session,
343339
ConfigurationProperties.DEFAULT_REPOSITORY_SYSTEM_DEPENDENCY_VISITOR,
344340
ConfigurationProperties.REPOSITORY_SYSTEM_DEPENDENCY_VISITOR);
345341
switch (strategy) {
346342
case PreorderDependencyNodeConsumerVisitor.NAME:
347-
return new PreorderDependencyNodeConsumerVisitor(nodeConsumer);
343+
return new PreorderDependencyNodeConsumerVisitor(nodeConsumer, dependencyFilter);
348344
case PostorderDependencyNodeConsumerVisitor.NAME:
349-
return new PostorderDependencyNodeConsumerVisitor(nodeConsumer);
345+
return new PostorderDependencyNodeConsumerVisitor(nodeConsumer, dependencyFilter);
350346
case LevelOrderDependencyNodeConsumerVisitor.NAME:
351-
return new LevelOrderDependencyNodeConsumerVisitor(nodeConsumer);
347+
return new LevelOrderDependencyNodeConsumerVisitor(nodeConsumer, dependencyFilter);
352348
default:
353349
throw new IllegalArgumentException("Invalid dependency visitor strategy: " + strategy);
354350
}

maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/AbstractDependencyNodeConsumerVisitor.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,35 @@
2222
import java.util.Map;
2323
import java.util.function.Consumer;
2424

25+
import org.eclipse.aether.graph.DependencyFilter;
2526
import org.eclipse.aether.graph.DependencyNode;
2627
import org.eclipse.aether.graph.DependencyVisitor;
2728

2829
import static java.util.Objects.requireNonNull;
2930

3031
/**
3132
* Abstract base class for dependency tree traverses that feed {@link Consumer<DependencyNode>}.
33+
* <p>
34+
* <strong>Implementations derived from this class cannot be embedded into {@link FilteringDependencyVisitor}</strong>,
35+
* this is why these classes accept {@link DependencyFilter} in constructor instead.
3236
*
3337
* @since 2.0.0
3438
*/
3539
abstract class AbstractDependencyNodeConsumerVisitor implements DependencyVisitor {
36-
protected final Consumer<DependencyNode> nodeConsumer;
40+
private static final DependencyFilter ACCEPT_ALL = (d, p) -> true;
41+
42+
private final Consumer<DependencyNode> nodeConsumer;
43+
44+
private final DependencyFilter filter;
45+
46+
private final Stack<DependencyNode> path;
3747

3848
private final Map<DependencyNode, Object> visitedNodes;
3949

40-
protected AbstractDependencyNodeConsumerVisitor(Consumer<DependencyNode> nodeConsumer) {
50+
protected AbstractDependencyNodeConsumerVisitor(Consumer<DependencyNode> nodeConsumer, DependencyFilter filter) {
4151
this.nodeConsumer = requireNonNull(nodeConsumer);
52+
this.filter = filter == null ? ACCEPT_ALL : filter;
53+
this.path = new Stack<>();
4254
this.visitedNodes = new IdentityHashMap<>(512);
4355
}
4456

@@ -53,8 +65,24 @@ protected boolean setVisited(DependencyNode node) {
5365
}
5466

5567
@Override
56-
public abstract boolean visitEnter(DependencyNode node);
68+
public final boolean visitEnter(DependencyNode node) {
69+
path.push(node);
70+
return doVisitEnter(node);
71+
}
72+
73+
protected abstract boolean doVisitEnter(DependencyNode node);
5774

5875
@Override
59-
public abstract boolean visitLeave(DependencyNode node);
76+
public final boolean visitLeave(DependencyNode node) {
77+
path.pop();
78+
return doVisitLeave(node);
79+
}
80+
81+
protected abstract boolean doVisitLeave(DependencyNode node);
82+
83+
protected void mayConsume(DependencyNode node) {
84+
if (filter.accept(node, path)) {
85+
nodeConsumer.accept(node);
86+
}
87+
}
6088
}

maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/LevelOrderDependencyNodeConsumerVisitor.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@
2323
import java.util.function.Consumer;
2424

2525
import org.eclipse.aether.ConfigurationProperties;
26+
import org.eclipse.aether.graph.DependencyFilter;
2627
import org.eclipse.aether.graph.DependencyNode;
2728

2829
/**
2930
* Processes dependency graph by traversing the graph in level order. This visitor visits each node exactly once
3031
* regardless how many paths within the dependency graph lead to the node such that the resulting node sequence is
3132
* free of duplicates.
33+
* <p>
34+
* <strong>Instances of this class cannot be embedded into {@link FilteringDependencyVisitor}</strong>, pass in the
35+
* filter {@link DependencyFilter} into constructor instead.
3236
*
3337
* @see NodeListGenerator
3438
* @since 2.0.0
@@ -45,13 +49,22 @@ public final class LevelOrderDependencyNodeConsumerVisitor extends AbstractDepen
4549
* Creates a new level order list generator.
4650
*/
4751
public LevelOrderDependencyNodeConsumerVisitor(Consumer<DependencyNode> nodeConsumer) {
48-
super(nodeConsumer);
52+
this(nodeConsumer, null);
53+
}
54+
55+
/**
56+
* Creates a new level order list generator with filter.
57+
*
58+
* @since 2.0.12
59+
*/
60+
public LevelOrderDependencyNodeConsumerVisitor(Consumer<DependencyNode> nodeConsumer, DependencyFilter filter) {
61+
super(nodeConsumer, filter);
4962
nodesPerLevel = new HashMap<>(16);
5063
visits = new Stack<>();
5164
}
5265

5366
@Override
54-
public boolean visitEnter(DependencyNode node) {
67+
protected boolean doVisitEnter(DependencyNode node) {
5568
boolean visited = !setVisited(node);
5669
visits.push(visited);
5770
if (!visited) {
@@ -61,14 +74,14 @@ public boolean visitEnter(DependencyNode node) {
6174
}
6275

6376
@Override
64-
public boolean visitLeave(DependencyNode node) {
77+
protected boolean doVisitLeave(DependencyNode node) {
6578
Boolean visited = visits.pop();
6679
if (visited) {
6780
return true;
6881
}
6982
if (visits.isEmpty()) {
7083
for (int l = 1; nodesPerLevel.containsKey(l); l++) {
71-
nodesPerLevel.get(l).forEach(nodeConsumer);
84+
nodesPerLevel.get(l).forEach(this::mayConsume);
7285
}
7386
}
7487
return true;

maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PostorderDependencyNodeConsumerVisitor.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121
import java.util.function.Consumer;
2222

2323
import org.eclipse.aether.ConfigurationProperties;
24+
import org.eclipse.aether.graph.DependencyFilter;
2425
import org.eclipse.aether.graph.DependencyNode;
2526

2627
/**
2728
* Processes dependency graph by traversing the graph in postorder. This visitor visits each node exactly once
2829
* regardless how many paths within the dependency graph lead to the node such that the resulting node sequence is
2930
* free of duplicates.
31+
* <p>
32+
* <strong>Instances of this class cannot be embedded into {@link FilteringDependencyVisitor}</strong>, pass in the
33+
* filter {@link DependencyFilter} into constructor instead.
3034
*
3135
* @see NodeListGenerator
3236
* @since 2.0.0
@@ -41,24 +45,33 @@ public final class PostorderDependencyNodeConsumerVisitor extends AbstractDepend
4145
* Creates a new postorder list generator.
4246
*/
4347
public PostorderDependencyNodeConsumerVisitor(Consumer<DependencyNode> nodeConsumer) {
44-
super(nodeConsumer);
48+
this(nodeConsumer, null);
49+
}
50+
51+
/**
52+
* Creates a new postorder list generator.
53+
*
54+
* @since 2.0.12
55+
*/
56+
public PostorderDependencyNodeConsumerVisitor(Consumer<DependencyNode> nodeConsumer, DependencyFilter filter) {
57+
super(nodeConsumer, filter);
4558
visits = new Stack<>();
4659
}
4760

4861
@Override
49-
public boolean visitEnter(DependencyNode node) {
62+
protected boolean doVisitEnter(DependencyNode node) {
5063
boolean visited = !setVisited(node);
5164
visits.push(visited);
5265
return !visited;
5366
}
5467

5568
@Override
56-
public boolean visitLeave(DependencyNode node) {
69+
protected boolean doVisitLeave(DependencyNode node) {
5770
Boolean visited = visits.pop();
5871
if (visited) {
5972
return true;
6073
}
61-
nodeConsumer.accept(node);
74+
mayConsume(node);
6275
return true;
6376
}
6477
}

maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/PreorderDependencyNodeConsumerVisitor.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121
import java.util.function.Consumer;
2222

2323
import org.eclipse.aether.ConfigurationProperties;
24+
import org.eclipse.aether.graph.DependencyFilter;
2425
import org.eclipse.aether.graph.DependencyNode;
2526

2627
/**
2728
* Processes dependency graph by traversing the graph in preorder. This visitor visits each node exactly once
2829
* regardless how many paths within the dependency graph lead to the node such that the resulting node sequence is
2930
* free of duplicates.
31+
* <p>
32+
* <strong>Instances of this class cannot be embedded into {@link FilteringDependencyVisitor}</strong>, pass in the
33+
* filter {@link DependencyFilter} into constructor instead.
3034
*
3135
* @see NodeListGenerator
3236
* @since 2.0.0
@@ -39,20 +43,29 @@ public final class PreorderDependencyNodeConsumerVisitor extends AbstractDepende
3943
* Creates a new preorder list generator.
4044
*/
4145
public PreorderDependencyNodeConsumerVisitor(Consumer<DependencyNode> nodeConsumer) {
42-
super(nodeConsumer);
46+
this(nodeConsumer, null);
47+
}
48+
49+
/**
50+
* Creates a new preorder list generator.
51+
*
52+
* @since 2.0.12
53+
*/
54+
public PreorderDependencyNodeConsumerVisitor(Consumer<DependencyNode> nodeConsumer, DependencyFilter filter) {
55+
super(nodeConsumer, filter);
4356
}
4457

4558
@Override
46-
public boolean visitEnter(DependencyNode node) {
59+
protected boolean doVisitEnter(DependencyNode node) {
4760
if (!setVisited(node)) {
4861
return false;
4962
}
50-
nodeConsumer.accept(node);
63+
mayConsume(node);
5164
return true;
5265
}
5366

5467
@Override
55-
public boolean visitLeave(DependencyNode node) {
68+
protected boolean doVisitLeave(DependencyNode node) {
5669
return true;
5770
}
5871
}

maven-resolver-util/src/test/java/org/eclipse/aether/util/graph/visitor/NodeListGeneratorTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,4 +218,40 @@ public boolean visitLeave(DependencyNode node) {
218218
assertEquals(3, nodeListGenerator.getFiles().size());
219219
assertEquals(fileNames, classPathNames);
220220
}
221+
222+
@Test
223+
void testPreOrderDuplicateSuppressionWithFilteringSimple() throws Exception {
224+
DependencyNode root = parse("simple.txt");
225+
226+
NodeListGenerator nodeListGenerator = new NodeListGenerator();
227+
PreorderDependencyNodeConsumerVisitor visitor = new PreorderDependencyNodeConsumerVisitor(
228+
nodeListGenerator, (d, p) -> !"a".equals(d.getArtifact().getArtifactId()));
229+
root.accept(visitor);
230+
231+
assertSequence(nodeListGenerator.getNodes(), "b", "c", "d", "e");
232+
}
233+
234+
@Test
235+
void testPostOrderDuplicateSuppressionWithFilteringSimple() throws Exception {
236+
DependencyNode root = parse("simple.txt");
237+
238+
NodeListGenerator nodeListGenerator = new NodeListGenerator();
239+
PostorderDependencyNodeConsumerVisitor visitor = new PostorderDependencyNodeConsumerVisitor(
240+
nodeListGenerator, (d, p) -> !"a".equals(d.getArtifact().getArtifactId()));
241+
root.accept(visitor);
242+
243+
assertSequence(nodeListGenerator.getNodes(), "c", "b", "e", "d");
244+
}
245+
246+
@Test
247+
void testLevelOrderDuplicateSuppressionWithFilteringSimple() throws Exception {
248+
DependencyNode root = parse("simple.txt");
249+
250+
NodeListGenerator nodeListGenerator = new NodeListGenerator();
251+
LevelOrderDependencyNodeConsumerVisitor visitor = new LevelOrderDependencyNodeConsumerVisitor(
252+
nodeListGenerator, (d, p) -> !"a".equals(d.getArtifact().getArtifactId()));
253+
root.accept(visitor);
254+
255+
assertSequence(nodeListGenerator.getNodes(), "b", "d", "c", "e");
256+
}
221257
}

src/site/markdown/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ To modify this file, edit the template and regenerate.
116116
| `"aether.syncContext.named.retry.wait"` | `Long` | The amount of milliseconds to wait between retries on time-out. | `200l` | 1.7.0 | No | Session Configuration |
117117
| `"aether.syncContext.named.time"` | `Long` | The maximum of time amount to be blocked to obtain lock. | `30l` | 1.7.0 | No | Session Configuration |
118118
| `"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 |
119-
| `"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 |
119+
| `"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 |
120120
| `"aether.transport.apache.followRedirects"` | `Boolean` | If enabled, Apache HttpClient will follow HTTP redirects. | `true` | 2.0.2 | Yes | Session Configuration |
121121
| `"aether.transport.apache.https.cipherSuites"` | `String` | Comma-separated list of <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#ciphersuites">Cipher Suites</a> which are enabled for HTTPS connections. | - | 2.0.0 | No | Session Configuration |
122122
| `"aether.transport.apache.https.protocols"` | `String` | Comma-separated list of <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#jssenames">Protocols </a> which are enabled for HTTPS connections. | - | 2.0.0 | No | Session Configuration |

0 commit comments

Comments
 (0)