You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by cs...@apache.org on 2022/03/17 13:27:26 UTC

[maven-resolver] 01/01: DependencyCollector: DF vs BF coexists

This is an automated email from the ASF dual-hosted git repository.

cstamas pushed a commit to branch bf-df-coexitsts
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git

commit ac046e09093824fcb426d4b09db99e4f8c6412fb
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Thu Mar 17 14:25:28 2022 +0100

    DependencyCollector: DF vs BF coexists
---
 .../eclipse/aether/impl/guice/AetherModule.java    |  22 +
 .../impl/collect/CachingArtifactTypeRegistry.java  |   2 +-
 .../aether/internal/impl/collect/DataPool.java     |  52 +-
 .../DefaultDependencyCollectionContext.java        |   4 +-
 .../impl/collect/DefaultDependencyCollector.java   | 868 +--------------------
 ...efaultDependencyGraphTransformationContext.java |   4 +-
 .../impl/collect/DefaultVersionFilterContext.java  |   4 +-
 .../impl/collect/DependencyCollectorDelegate.java  |  29 +
 .../BfDependencyCollector.java}                    |  57 +-
 .../BfDependencyCycle.java}                        |   8 +-
 .../BfProcessingContext.java}                      |  10 +-
 .../DfDependencyCollector.java}                    | 237 +++---
 .../DfDependencyCycle.java}                        |  61 +-
 .../aether/internal/impl/collect/df/NodeStack.java | 127 +++
 .../BfDependencyCollectorTest.java}                |  25 +-
 .../BfDependencyCycleTest.java}                    |  12 +-
 .../DfDependencyCollectorTest.java}                |  26 +-
 .../DfDependencyCycleTest.java}                    |  15 +-
 18 files changed, 467 insertions(+), 1096 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java
index 9629dc9..2082422 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java
@@ -48,6 +48,9 @@ import org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory;
 import org.eclipse.aether.internal.impl.checksum.Sha256ChecksumAlgorithmFactory;
 import org.eclipse.aether.internal.impl.checksum.Sha512ChecksumAlgorithmFactory;
 import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector;
+import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
+import org.eclipse.aether.internal.impl.collect.bf.BfDependencyCollector;
+import org.eclipse.aether.internal.impl.collect.df.DfDependencyCollector;
 import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory;
 import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector;
 import org.eclipse.aether.internal.impl.synccontext.named.SimpleNamedLockFactorySelector;
@@ -132,8 +135,14 @@ public class AetherModule
                 .to( DefaultRepositorySystem.class ).in( Singleton.class );
         bind( ArtifactResolver.class ) //
                 .to( DefaultArtifactResolver.class ).in( Singleton.class );
+
         bind( DependencyCollector.class ) //
                 .to( DefaultDependencyCollector.class ).in( Singleton.class );
+        bind( DependencyCollectorDelegate.class ).annotatedWith( Names.named( BfDependencyCollector.NAME ) ) //
+                .to( BfDependencyCollector.class ).in( Singleton.class );
+        bind( DependencyCollectorDelegate.class ).annotatedWith( Names.named( DfDependencyCollector.NAME ) ) //
+                .to( DfDependencyCollector.class ).in( Singleton.class );
+
         bind( Deployer.class ) //
                 .to( DefaultDeployer.class ).in( Singleton.class );
         bind( Installer.class ) //
@@ -214,6 +223,19 @@ public class AetherModule
 
     @Provides
     @Singleton
+    Map<String, DependencyCollectorDelegate> provideDependencyCollectorDelegates(
+            @Named( BfDependencyCollector.NAME ) DependencyCollectorDelegate bf,
+            @Named( DfDependencyCollector.NAME ) DependencyCollectorDelegate df
+    )
+    {
+        Map<String, DependencyCollectorDelegate> providedDependencyCollectorDelegates = new HashMap<>();
+        providedDependencyCollectorDelegates.put( BfDependencyCollector.NAME, bf );
+        providedDependencyCollectorDelegates.put( DfDependencyCollector.NAME, df );
+        return providedDependencyCollectorDelegates;
+    }
+
+    @Provides
+    @Singleton
     Map<String, ProvidedChecksumsSource> provideChecksumSources(
         @Named( FileProvidedChecksumsSource.NAME ) ProvidedChecksumsSource fileProvidedChecksumSource
     )
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java
index a260234..bb03b14 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java
@@ -29,7 +29,7 @@ import org.eclipse.aether.artifact.ArtifactTypeRegistry;
 /**
  * A short-lived artifact type registry that caches results from a presumedly slower type registry.
  */
-class CachingArtifactTypeRegistry
+public class CachingArtifactTypeRegistry
     implements ArtifactTypeRegistry
 {
 
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
index 4a14555..6190c76 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
@@ -48,8 +48,9 @@ import org.eclipse.aether.version.Version;
 import org.eclipse.aether.version.VersionConstraint;
 
 /**
+ * DataPool.
  */
-final class DataPool
+public final class DataPool
 {
 
     private static final String ARTIFACT_POOL = DataPool.class.getName() + "$Artifact";
@@ -58,7 +59,7 @@ final class DataPool
 
     private static final String DESCRIPTORS = DataPool.class.getName() + "$Descriptors";
 
-    static final ArtifactDescriptorResult NO_DESCRIPTOR =
+    public static final ArtifactDescriptorResult NO_DESCRIPTOR =
         new ArtifactDescriptorResult( new ArtifactDescriptorRequest() );
 
     private ObjectPool<Artifact> artifacts;
@@ -72,7 +73,7 @@ final class DataPool
     private final Map<Object, List<DependencyNode>> nodes = new HashMap<>( 256 );
 
     @SuppressWarnings( "unchecked" )
-    DataPool( RepositorySystemSession session )
+    public DataPool( RepositorySystemSession session )
     {
         RepositoryCache cache = session.getCache();
 
@@ -121,12 +122,12 @@ final class DataPool
         return dependencies.intern( dependency );
     }
 
-    Object toKey( ArtifactDescriptorRequest request )
+    public Object toKey( ArtifactDescriptorRequest request )
     {
         return request.getArtifact();
     }
 
-    ArtifactDescriptorResult getDescriptor( Object key, ArtifactDescriptorRequest request )
+    public ArtifactDescriptorResult getDescriptor( Object key, ArtifactDescriptorRequest request )
     {
         Descriptor descriptor = descriptors.get( key );
         if ( descriptor != null )
@@ -136,22 +137,22 @@ final class DataPool
         return null;
     }
 
-    void putDescriptor( Object key, ArtifactDescriptorResult result )
+    public void putDescriptor( Object key, ArtifactDescriptorResult result )
     {
         descriptors.put( key, new GoodDescriptor( result ) );
     }
 
-    void putDescriptor( Object key, ArtifactDescriptorException e )
+    public void putDescriptor( Object key, ArtifactDescriptorException e )
     {
         descriptors.put( key, BadDescriptor.INSTANCE );
     }
 
-    Object toKey( VersionRangeRequest request )
+    public Object toKey( VersionRangeRequest request )
     {
         return new ConstraintKey( request );
     }
 
-    VersionRangeResult getConstraint( Object key, VersionRangeRequest request )
+    public VersionRangeResult getConstraint( Object key, VersionRangeRequest request )
     {
         Constraint constraint = constraints.get( key );
         if ( constraint != null )
@@ -161,7 +162,7 @@ final class DataPool
         return null;
     }
 
-    void putConstraint( Object key, VersionRangeResult result )
+    public void putConstraint( Object key, VersionRangeResult result )
     {
         constraints.put( key, new Constraint( result ) );
     }
@@ -182,14 +183,20 @@ final class DataPool
         nodes.put( key, children );
     }
 
-    abstract static class Descriptor
+    /**
+     * Descriptor
+     */
+    public abstract static class Descriptor
     {
 
         public abstract ArtifactDescriptorResult toResult( ArtifactDescriptorRequest request );
 
     }
 
-    static final class GoodDescriptor
+    /**
+     * GoodDescriptor
+     */
+    public static final class GoodDescriptor
         extends Descriptor
     {
 
@@ -205,7 +212,7 @@ final class DataPool
 
         final List<Dependency> managedDependencies;
 
-        GoodDescriptor( ArtifactDescriptorResult result )
+        public GoodDescriptor( ArtifactDescriptorResult result )
         {
             artifact = result.getArtifact();
             relocations = result.getRelocations();
@@ -229,7 +236,10 @@ final class DataPool
 
     }
 
-    static final class BadDescriptor
+    /**
+     * BadDescriptor
+     */
+    public static final class BadDescriptor
         extends Descriptor
     {
 
@@ -285,7 +295,10 @@ final class DataPool
         }
     }
 
-    static final class ConstraintKey
+    /**
+     * ConstraintKey
+     */
+    public static final class ConstraintKey
     {
         private final Artifact artifact;
 
@@ -293,7 +306,7 @@ final class DataPool
 
         private final int hashCode;
 
-        ConstraintKey( VersionRangeRequest request )
+        public ConstraintKey( VersionRangeRequest request )
         {
             artifact = request.getArtifact();
             repositories = request.getRepositories();
@@ -360,7 +373,10 @@ final class DataPool
         }
     }
 
-    static final class GraphKey
+    /**
+     * GraphKey
+     */
+    public static final class GraphKey
     {
         private final Artifact artifact;
 
@@ -376,7 +392,7 @@ final class DataPool
 
         private final int hashCode;
 
-        GraphKey( Artifact artifact, List<RemoteRepository> repositories, DependencySelector selector,
+        public GraphKey( Artifact artifact, List<RemoteRepository> repositories, DependencySelector selector,
                   DependencyManager manager, DependencyTraverser traverser, VersionFilter filter )
         {
             this.artifact = artifact;
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java
index 3bf4fe1..0c66fa7 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java
@@ -29,7 +29,7 @@ import org.eclipse.aether.graph.Dependency;
 /**
  * @see DefaultDependencyCollector
  */
-final class DefaultDependencyCollectionContext
+public final class DefaultDependencyCollectionContext
     implements DependencyCollectionContext
 {
 
@@ -41,7 +41,7 @@ final class DefaultDependencyCollectionContext
 
     private List<Dependency> managedDependencies;
 
-    DefaultDependencyCollectionContext( RepositorySystemSession session, Artifact artifact,
+    public DefaultDependencyCollectionContext( RepositorySystemSession session, Artifact artifact,
                                                Dependency dependency, List<Dependency> managedDependencies )
     {
         this.session = session;
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
index a773367..0dba4e4 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
@@ -19,875 +19,79 @@ package org.eclipse.aether.internal.impl.collect;
  * under the License.
  */
 
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-
-import static java.util.Objects.requireNonNull;
-import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find;
-
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import org.eclipse.aether.DefaultRepositorySystemSession;
-import org.eclipse.aether.RepositoryException;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.eclipse.aether.RepositorySystemSession;
-import org.eclipse.aether.RequestTrace;
-import org.eclipse.aether.artifact.Artifact;
-import org.eclipse.aether.artifact.ArtifactProperties;
 import org.eclipse.aether.collection.CollectRequest;
 import org.eclipse.aether.collection.CollectResult;
 import org.eclipse.aether.collection.DependencyCollectionException;
-import org.eclipse.aether.collection.DependencyGraphTransformer;
-import org.eclipse.aether.collection.DependencyManagement;
-import org.eclipse.aether.collection.DependencyManager;
-import org.eclipse.aether.collection.DependencySelector;
-import org.eclipse.aether.collection.DependencyTraverser;
-import org.eclipse.aether.collection.VersionFilter;
-import org.eclipse.aether.graph.DefaultDependencyNode;
-import org.eclipse.aether.graph.Dependency;
-import org.eclipse.aether.graph.DependencyNode;
-import org.eclipse.aether.graph.Exclusion;
-import org.eclipse.aether.impl.ArtifactDescriptorReader;
 import org.eclipse.aether.impl.DependencyCollector;
-import org.eclipse.aether.impl.RemoteRepositoryManager;
-import org.eclipse.aether.impl.VersionRangeResolver;
-import org.eclipse.aether.repository.ArtifactRepository;
-import org.eclipse.aether.repository.RemoteRepository;
-import org.eclipse.aether.resolution.ArtifactDescriptorException;
-import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
-import org.eclipse.aether.resolution.ArtifactDescriptorResult;
-import org.eclipse.aether.resolution.VersionRangeRequest;
-import org.eclipse.aether.resolution.VersionRangeResolutionException;
-import org.eclipse.aether.resolution.VersionRangeResult;
+import org.eclipse.aether.internal.impl.collect.bf.BfDependencyCollector;
+import org.eclipse.aether.internal.impl.collect.df.DfDependencyCollector;
 import org.eclipse.aether.spi.locator.Service;
 import org.eclipse.aether.spi.locator.ServiceLocator;
 import org.eclipse.aether.util.ConfigUtils;
-import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
-import org.eclipse.aether.util.graph.transformer.TransformationContextKeys;
-import org.eclipse.aether.version.Version;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import static java.util.Objects.requireNonNull;
 
 /**
+ *
  */
 @Singleton
 @Named
 public class DefaultDependencyCollector
-    implements DependencyCollector, Service
+        implements DependencyCollector, Service
 {
+    private static final String CONFIG_PROP_COLLECTOR_IMPL = "aether.collector.impl";
 
-    private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
-
-    private static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50;
-
-    private static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles";
-
-    private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10;
-
-    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyCollector.class );
+    private static final String DEFAULT_COLLECTOR_IMPL = DfDependencyCollector.NAME;
 
-    private RemoteRepositoryManager remoteRepositoryManager;
-
-    private ArtifactDescriptorReader descriptorReader;
-
-    private VersionRangeResolver versionRangeResolver;
+    private final Map<String, DependencyCollectorDelegate> delegates;
 
+    /**
+     * Default ctor for SL.
+     *
+     * @deprecated SL is to be removed.
+     */
+    @Deprecated
     public DefaultDependencyCollector()
     {
-        // enables default constructor
+       this.delegates = new HashMap<>();
     }
 
     @Inject
-    DefaultDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
-                                ArtifactDescriptorReader artifactDescriptorReader,
-                                VersionRangeResolver versionRangeResolver )
+    public DefaultDependencyCollector( Map<String, DependencyCollectorDelegate> delegates )
     {
-        setRemoteRepositoryManager( remoteRepositoryManager );
-        setArtifactDescriptorReader( artifactDescriptorReader );
-        setVersionRangeResolver( versionRangeResolver );
+        this.delegates = requireNonNull( delegates );
     }
 
+    @Override
     public void initService( ServiceLocator locator )
     {
-        setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
-        setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) );
-        setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
-    }
-
-    public DefaultDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
-    {
-        this.remoteRepositoryManager =
-                requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" );
-        return this;
-    }
-
-    public DefaultDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
-    {
-        descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" );
-        return this;
-    }
-
-    public DefaultDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
-    {
-        this.versionRangeResolver =
-                requireNonNull( versionRangeResolver, "version range resolver cannot be null" );
-        return this;
+        BfDependencyCollector bf = new BfDependencyCollector();
+        bf.initService( locator );
+        DfDependencyCollector df = new DfDependencyCollector();
+        df.initService( locator );
+        this.delegates.put( BfDependencyCollector.NAME, bf );
+        this.delegates.put( DfDependencyCollector.NAME, df );
     }
 
-    @SuppressWarnings( "checkstyle:methodlength" )
+    @Override
     public CollectResult collectDependencies( RepositorySystemSession session, CollectRequest request )
-        throws DependencyCollectionException
+            throws DependencyCollectionException
     {
-        requireNonNull( session, "session cannot be null" );
-        requireNonNull( request, "request cannot be null" );
-        session = optimizeSession( session );
-
-        RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
-
-        CollectResult result = new CollectResult( request );
-
-        DependencySelector depSelector = session.getDependencySelector();
-        DependencyManager depManager = session.getDependencyManager();
-        DependencyTraverser depTraverser = session.getDependencyTraverser();
-        VersionFilter verFilter = session.getVersionFilter();
-
-        Dependency root = request.getRoot();
-        List<RemoteRepository> repositories = request.getRepositories();
-        List<Dependency> dependencies = request.getDependencies();
-        List<Dependency> managedDependencies = request.getManagedDependencies();
-
-        Map<String, Object> stats = new LinkedHashMap<>();
-        long time1 = System.nanoTime();
-
-        DefaultDependencyNode node;
-        if ( root != null )
-        {
-            List<? extends Version> versions;
-            VersionRangeResult rangeResult;
-            try
-            {
-                VersionRangeRequest rangeRequest =
-                    new VersionRangeRequest( root.getArtifact(), request.getRepositories(),
-                                             request.getRequestContext() );
-                rangeRequest.setTrace( trace );
-                rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest );
-                versions = filterVersions( root, rangeResult, verFilter, new DefaultVersionFilterContext( session ) );
-            }
-            catch ( VersionRangeResolutionException e )
-            {
-                result.addException( e );
-                throw new DependencyCollectionException( result, e.getMessage() );
-            }
-
-            Version version = versions.get( versions.size() - 1 );
-            root = root.setArtifact( root.getArtifact().setVersion( version.toString() ) );
-
-            ArtifactDescriptorResult descriptorResult;
-            try
-            {
-                ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
-                descriptorRequest.setArtifact( root.getArtifact() );
-                descriptorRequest.setRepositories( request.getRepositories() );
-                descriptorRequest.setRequestContext( request.getRequestContext() );
-                descriptorRequest.setTrace( trace );
-                if ( isLackingDescriptor( root.getArtifact() ) )
-                {
-                    descriptorResult = new ArtifactDescriptorResult( descriptorRequest );
-                }
-                else
-                {
-                    descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
-                }
-            }
-            catch ( ArtifactDescriptorException e )
-            {
-                result.addException( e );
-                throw new DependencyCollectionException( result, e.getMessage() );
-            }
-
-            root = root.setArtifact( descriptorResult.getArtifact() );
-
-            if ( !session.isIgnoreArtifactDescriptorRepositories() )
-            {
-                repositories = remoteRepositoryManager.aggregateRepositories( session, repositories,
-                                                                              descriptorResult.getRepositories(),
-                                                                              true );
-            }
-            dependencies = mergeDeps( dependencies, descriptorResult.getDependencies() );
-            managedDependencies = mergeDeps( managedDependencies, descriptorResult.getManagedDependencies() );
-
-            node = new DefaultDependencyNode( root );
-            node.setRequestContext( request.getRequestContext() );
-            node.setRelocations( descriptorResult.getRelocations() );
-            node.setVersionConstraint( rangeResult.getVersionConstraint() );
-            node.setVersion( version );
-            node.setAliases( descriptorResult.getAliases() );
-            node.setRepositories( request.getRepositories() );
-        }
-        else
-        {
-            node = new DefaultDependencyNode( request.getRootArtifact() );
-            node.setRequestContext( request.getRequestContext() );
-            node.setRepositories( request.getRepositories() );
-        }
-
-        result.setRoot( node );
-
-        boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency( root );
-        String errorPath = null;
-        if ( traverse && !dependencies.isEmpty() )
+        String delegateName = ConfigUtils.getString( session, DEFAULT_COLLECTOR_IMPL, CONFIG_PROP_COLLECTOR_IMPL );
+        DependencyCollectorDelegate delegate = delegates.get( delegateName );
+        if ( delegate == null )
         {
-            DataPool pool = new DataPool( session );
-
-            DefaultDependencyCollectionContext context =
-                new DefaultDependencyCollectionContext( session, request.getRootArtifact(), root, managedDependencies );
-
-            DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext( session );
-
-            Args args = new Args( session, trace, pool, context, versionContext, request );
-            Results results = new Results( result, session );
-
-            DependencySelector rootDepSelector =
-                    depSelector != null ? depSelector.deriveChildSelector( context ) : null;
-            DependencyManager rootDepManager = depManager != null ? depManager.deriveChildManager( context ) : null;
-            DependencyTraverser rootDepTraverser =
-                    depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null;
-            VersionFilter rootVerFilter = verFilter != null ? verFilter.deriveChildFilter( context ) : null;
-
-            for ( Dependency dependency : dependencies )
-            {
-                args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser,
-                                rootVerFilter, repositories, managedDependencies, Collections.singletonList( node ),
-                                dependency ) );
-            }
-
-            while ( !args.dependencyProcessingQueue.isEmpty() )
-            {
-                processDependency( args, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(),
-                        false );
-            }
-
-            errorPath = results.errorPath;
-        }
-
-        long time2 = System.nanoTime();
-
-        DependencyGraphTransformer transformer = session.getDependencyGraphTransformer();
-        if ( transformer != null )
-        {
-            try
-            {
-                DefaultDependencyGraphTransformationContext context =
-                    new DefaultDependencyGraphTransformationContext( session );
-                context.put( TransformationContextKeys.STATS, stats );
-                result.setRoot( transformer.transformGraph( node, context ) );
-            }
-            catch ( RepositoryException e )
-            {
-                result.addException( e );
-            }
-        }
-
-        long time3 = System.nanoTime();
-        stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 );
-        stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 );
-        LOGGER.debug( "Dependency collection stats {}", stats );
-
-        if ( errorPath != null )
-        {
-            throw new DependencyCollectionException( result, "Failed to collect dependencies at " + errorPath );
-        }
-        if ( !result.getExceptions().isEmpty() )
-        {
-            throw new DependencyCollectionException( result );
-        }
-
-        return result;
-    }
-
-    private static RepositorySystemSession optimizeSession( RepositorySystemSession session )
-    {
-        DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession( session );
-        optimized.setArtifactTypeRegistry( CachingArtifactTypeRegistry.newInstance( session ) );
-        return optimized;
-    }
-
-    private List<Dependency> mergeDeps( List<Dependency> dominant, List<Dependency> recessive )
-    {
-        List<Dependency> result;
-        if ( dominant == null || dominant.isEmpty() )
-        {
-            result = recessive;
-        }
-        else if ( recessive == null || recessive.isEmpty() )
-        {
-            result = dominant;
-        }
-        else
-        {
-            int initialCapacity = dominant.size() + recessive.size();
-            result = new ArrayList<>( initialCapacity );
-            Collection<String> ids = new HashSet<>( initialCapacity, 1.0f );
-            for ( Dependency dependency : dominant )
-            {
-                ids.add( getId( dependency.getArtifact() ) );
-                result.add( dependency );
-            }
-            for ( Dependency dependency : recessive )
-            {
-                if ( !ids.contains( getId( dependency.getArtifact() ) ) )
-                {
-                    result.add( dependency );
-                }
-            }
-        }
-        return result;
-    }
-
-    private static String getId( Artifact a )
-    {
-        return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension();
-    }
-
-    @SuppressWarnings( "checkstyle:parameternumber" )
-    private void processDependency( Args args, Results results, DependencyProcessingContext context,
-                                    List<Artifact> relocations, boolean disableVersionManagement )
-    {
-
-        if ( context.depSelector != null && !context.depSelector.selectDependency( context.dependency ) )
-        {
-            return;
-        }
-
-        PremanagedDependency preManaged =
-                PremanagedDependency.create( context.depManager, context.dependency, disableVersionManagement,
-                        args.premanagedState );
-        Dependency dependency = preManaged.managedDependency;
-
-        boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() );
-
-        boolean traverse =
-                !noDescriptor && ( context.depTraverser == null || context.depTraverser.traverseDependency(
-                        dependency ) );
-
-        List<? extends Version> versions;
-        VersionRangeResult rangeResult;
-        try
-        {
-            VersionRangeRequest rangeRequest = createVersionRangeRequest( args, context.repositories, dependency );
-
-            rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
-
-            versions = filterVersions( dependency, rangeResult, context.verFilter, args.versionContext );
-        }
-        catch ( VersionRangeResolutionException e )
-        {
-            results.addException( dependency, e, context.parents );
-            return;
-        }
-
-        for ( Version version : versions )
-        {
-            Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() );
-            Dependency d = dependency.setArtifact( originalArtifact );
-
-            ArtifactDescriptorRequest descriptorRequest =
-                    createArtifactDescriptorRequest( args, context.repositories, d );
-
-            final ArtifactDescriptorResult descriptorResult =
-                    noDescriptor
-                            ? new ArtifactDescriptorResult( descriptorRequest )
-                            : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session,
-                                    context.withDependency( d ), results );
-
-            if ( descriptorResult != null )
-            {
-                d = d.setArtifact( descriptorResult.getArtifact() );
-
-                int cycleEntry = find( context.parents, d.getArtifact() );
-                if ( cycleEntry >= 0 )
-                {
-                    results.addCycle( context.parents, cycleEntry, d );
-                    DependencyNode cycleNode = context.parents.get( cycleEntry );
-                    if ( cycleNode.getDependency() != null )
-                    {
-                        DefaultDependencyNode child =
-                                createDependencyNode( relocations, preManaged, rangeResult, version, d,
-                                        descriptorResult, cycleNode );
-                        context.getParent().getChildren().add( child );
-                        continue;
-                    }
-                }
-
-                if ( !descriptorResult.getRelocations().isEmpty() )
-                {
-                    boolean disableVersionManagementSubsequently =
-                        originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
-                            && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
-
-                    processDependency( args, results, context.withDependency( d ), descriptorResult.getRelocations(),
-                            disableVersionManagementSubsequently );
-                    return;
-                }
-                else
-                {
-                    d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) );
-
-                    List<RemoteRepository> repos =
-                        getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
-
-                    DefaultDependencyNode child =
-                        createDependencyNode( relocations, preManaged, rangeResult, version, d,
-                                              descriptorResult.getAliases(), repos, args.request.getRequestContext() );
-
-                    context.getParent().getChildren().add( child );
-
-                    boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
-                    if ( recurse )
-                    {
-                        doRecurse( args, context.withDependency( d ), descriptorResult, child );
-                    }
-                }
-            }
-            else
-            {
-                List<RemoteRepository> repos =
-                    getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
-                DefaultDependencyNode child =
-                    createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos,
-                                          args.request.getRequestContext() );
-                context.getParent().getChildren().add( child );
-            }
+            throw new IllegalArgumentException( "Unknown collector impl: '" + delegateName
+                    + "', known implementations are " + delegates.keySet() );
         }
+        return delegate.collectDependencies( session, request );
     }
-
-    @SuppressWarnings( "checkstyle:parameternumber" )
-    private void doRecurse( Args args, DependencyProcessingContext parentContext,
-                            ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
-    {
-        DefaultDependencyCollectionContext context = args.collectionContext;
-        context.set( parentContext.dependency, descriptorResult.getManagedDependencies() );
-
-        DependencySelector childSelector =
-                parentContext.depSelector != null ? parentContext.depSelector.deriveChildSelector( context ) : null;
-        DependencyManager childManager =
-                parentContext.depManager != null ? parentContext.depManager.deriveChildManager( context ) : null;
-        DependencyTraverser childTraverser =
-                parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser( context ) : null;
-        VersionFilter childFilter =
-                parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter( context ) : null;
-
-        final List<RemoteRepository> childRepos =
-                args.ignoreRepos
-                        ? parentContext.repositories
-                        : remoteRepositoryManager.aggregateRepositories( args.session, parentContext.repositories,
-                        descriptorResult.getRepositories(), true );
-
-        Object key =
-                args.pool.toKey( parentContext.dependency.getArtifact(), childRepos, childSelector, childManager,
-                        childTraverser, childFilter );
-
-        List<DependencyNode> children = args.pool.getChildren( key );
-        if ( children == null )
-        {
-            args.pool.putChildren( key, child.getChildren() );
-
-            List<DependencyNode> parents = new ArrayList<>( parentContext.parents );
-            parents.add( child );
-            for ( Dependency dependency : descriptorResult.getDependencies() )
-            {
-                args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( childSelector, childManager, childTraverser, childFilter,
-                                childRepos, descriptorResult.getManagedDependencies(), parents, dependency ) );
-            }
-        }
-        else
-        {
-            child.setChildren( children );
-        }
-    }
-
-    private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool,
-                                                                      ArtifactDescriptorRequest descriptorRequest,
-                                                                      RepositorySystemSession session,
-                                                                      DependencyProcessingContext context,
-                                                                      Results results )
-    {
-        Object key = pool.toKey( descriptorRequest );
-        ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest );
-        if ( descriptorResult == null )
-        {
-            try
-            {
-                descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
-                pool.putDescriptor( key, descriptorResult );
-            }
-            catch ( ArtifactDescriptorException e )
-            {
-                results.addException( context.dependency, e, context.parents );
-                pool.putDescriptor( key, e );
-                return null;
-            }
-
-        }
-        else if ( descriptorResult == DataPool.NO_DESCRIPTOR )
-        {
-            return null;
-        }
-
-        return descriptorResult;
-    }
-
-    @SuppressWarnings( "checkstyle:parameternumber" )
-    private static DefaultDependencyNode createDependencyNode( List<Artifact> relocations,
-                                                               PremanagedDependency preManaged,
-                                                               VersionRangeResult rangeResult, Version version,
-                                                               Dependency d, Collection<Artifact> aliases,
-                                                               List<RemoteRepository> repos, String requestContext )
-    {
-        DefaultDependencyNode child = new DefaultDependencyNode( d );
-        preManaged.applyTo( child );
-        child.setRelocations( relocations );
-        child.setVersionConstraint( rangeResult.getVersionConstraint() );
-        child.setVersion( version );
-        child.setAliases( aliases );
-        child.setRepositories( repos );
-        child.setRequestContext( requestContext );
-        return child;
-    }
-
-    private static DefaultDependencyNode createDependencyNode( List<Artifact> relocations,
-                                                               PremanagedDependency preManaged,
-                                                               VersionRangeResult rangeResult, Version version,
-                                                               Dependency d, ArtifactDescriptorResult descriptorResult,
-                                                               DependencyNode cycleNode )
-    {
-        DefaultDependencyNode child =
-            createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult.getAliases(),
-                                  cycleNode.getRepositories(), cycleNode.getRequestContext() );
-        child.setChildren( cycleNode.getChildren() );
-        return child;
-    }
-
-    private static ArtifactDescriptorRequest createArtifactDescriptorRequest( Args args,
-                                                                              List<RemoteRepository> repositories,
-                                                                              Dependency d )
-    {
-        ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
-        descriptorRequest.setArtifact( d.getArtifact() );
-        descriptorRequest.setRepositories( repositories );
-        descriptorRequest.setRequestContext( args.request.getRequestContext() );
-        descriptorRequest.setTrace( args.trace );
-        return descriptorRequest;
-    }
-
-    private static VersionRangeRequest createVersionRangeRequest( Args args, List<RemoteRepository> repositories,
-                                                                  Dependency dependency )
-    {
-        VersionRangeRequest rangeRequest = new VersionRangeRequest();
-        rangeRequest.setArtifact( dependency.getArtifact() );
-        rangeRequest.setRepositories( repositories );
-        rangeRequest.setRequestContext( args.request.getRequestContext() );
-        rangeRequest.setTrace( args.trace );
-        return rangeRequest;
-    }
-
-    private VersionRangeResult cachedResolveRangeResult( VersionRangeRequest rangeRequest, DataPool pool,
-                                                         RepositorySystemSession session )
-        throws VersionRangeResolutionException
-    {
-        Object key = pool.toKey( rangeRequest );
-        VersionRangeResult rangeResult = pool.getConstraint( key, rangeRequest );
-        if ( rangeResult == null )
-        {
-            rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest );
-            pool.putConstraint( key, rangeResult );
-        }
-        return rangeResult;
-    }
-
-    private static boolean isLackingDescriptor( Artifact artifact )
-    {
-        return artifact.getProperty( ArtifactProperties.LOCAL_PATH, null ) != null;
-    }
-
-    private static List<RemoteRepository> getRemoteRepositories( ArtifactRepository repository,
-                                                                 List<RemoteRepository> repositories )
-    {
-        if ( repository instanceof RemoteRepository )
-        {
-            return Collections.singletonList( (RemoteRepository) repository );
-        }
-        if ( repository != null )
-        {
-            return Collections.emptyList();
-        }
-        return repositories;
-    }
-
-    private static List<? extends Version> filterVersions( Dependency dependency, VersionRangeResult rangeResult,
-                                                           VersionFilter verFilter,
-                                                           DefaultVersionFilterContext verContext )
-        throws VersionRangeResolutionException
-    {
-        if ( rangeResult.getVersions().isEmpty() )
-        {
-            throw new VersionRangeResolutionException( rangeResult,
-                                                       "No versions available for " + dependency.getArtifact()
-                                                           + " within specified range" );
-        }
-
-        List<? extends Version> versions;
-        if ( verFilter != null && rangeResult.getVersionConstraint().getRange() != null )
-        {
-            verContext.set( dependency, rangeResult );
-            try
-            {
-                verFilter.filterVersions( verContext );
-            }
-            catch ( RepositoryException e )
-            {
-                throw new VersionRangeResolutionException( rangeResult,
-                                                           "Failed to filter versions for " + dependency.getArtifact()
-                                                               + ": " + e.getMessage(), e );
-            }
-            versions = verContext.get();
-            if ( versions.isEmpty() )
-            {
-                throw new VersionRangeResolutionException( rangeResult,
-                                                           "No acceptable versions for " + dependency.getArtifact()
-                                                               + ": " + rangeResult.getVersions() );
-            }
-        }
-        else
-        {
-            versions = rangeResult.getVersions();
-        }
-        return versions;
-    }
-
-    static class Args
-    {
-
-        final RepositorySystemSession session;
-
-        final boolean ignoreRepos;
-
-        final boolean premanagedState;
-
-        final RequestTrace trace;
-
-        final DataPool pool;
-
-        final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>();
-
-        final DefaultDependencyCollectionContext collectionContext;
-
-        final DefaultVersionFilterContext versionContext;
-
-        final CollectRequest request;
-
-        Args( RepositorySystemSession session, RequestTrace trace, DataPool pool,
-                     DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext,
-                     CollectRequest request )
-        {
-            this.session = session;
-            this.request = request;
-            this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories();
-            this.premanagedState = ConfigUtils.getBoolean( session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE );
-            this.trace = trace;
-            this.pool = pool;
-            this.collectionContext = collectionContext;
-            this.versionContext = versionContext;
-        }
-
-    }
-
-    static class Results
-    {
-
-        private final CollectResult result;
-
-        final int maxExceptions;
-
-        final int maxCycles;
-
-        String errorPath;
-
-        Results( CollectResult result, RepositorySystemSession session )
-        {
-            this.result = result;
-
-            maxExceptions =
-                    ConfigUtils.getInteger( session, CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT, CONFIG_PROP_MAX_EXCEPTIONS );
-
-            maxCycles = ConfigUtils.getInteger( session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES );
-        }
-
-        public void addException( Dependency dependency, Exception e, List<DependencyNode> nodes )
-        {
-            if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions )
-            {
-                result.addException( e );
-                if ( errorPath == null )
-                {
-                    StringBuilder buffer = new StringBuilder( 256 );
-                    for ( DependencyNode node : nodes )
-                    {
-                        if ( buffer.length() > 0 )
-                        {
-                            buffer.append( " -> " );
-                        }
-                        Dependency dep = node.getDependency();
-                        if ( dep != null )
-                        {
-                            buffer.append( dep.getArtifact() );
-                        }
-                    }
-                    if ( buffer.length() > 0 )
-                    {
-                        buffer.append( " -> " );
-                    }
-                    buffer.append( dependency.getArtifact() );
-                    errorPath = buffer.toString();
-                }
-            }
-        }
-
-        public void addCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
-        {
-            if ( maxCycles < 0 || result.getCycles().size() < maxCycles )
-            {
-                result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) );
-            }
-        }
-
-    }
-
-    static class PremanagedDependency
-    {
-
-        final String premanagedVersion;
-
-        final String premanagedScope;
-
-        final Boolean premanagedOptional;
-
-        /**
-         * @since 1.1.0
-         */
-        final Collection<Exclusion> premanagedExclusions;
-
-        /**
-         * @since 1.1.0
-         */
-        final Map<String, String> premanagedProperties;
-
-        final int managedBits;
-
-        final Dependency managedDependency;
-
-        final boolean premanagedState;
-
-        @SuppressWarnings( "checkstyle:parameternumber" )
-        PremanagedDependency( String premanagedVersion, String premanagedScope, Boolean premanagedOptional,
-                              Collection<Exclusion> premanagedExclusions, Map<String, String> premanagedProperties,
-                              int managedBits, Dependency managedDependency, boolean premanagedState )
-        {
-            this.premanagedVersion = premanagedVersion;
-            this.premanagedScope = premanagedScope;
-            this.premanagedOptional = premanagedOptional;
-            this.premanagedExclusions =
-                premanagedExclusions != null
-                    ? Collections.unmodifiableCollection( new ArrayList<>( premanagedExclusions ) )
-                    : null;
-
-            this.premanagedProperties =
-                premanagedProperties != null
-                    ? Collections.unmodifiableMap( new HashMap<>( premanagedProperties ) )
-                    : null;
-
-            this.managedBits = managedBits;
-            this.managedDependency = managedDependency;
-            this.premanagedState = premanagedState;
-        }
-
-        static PremanagedDependency create( DependencyManager depManager, Dependency dependency,
-                                            boolean disableVersionManagement, boolean premanagedState )
-        {
-            DependencyManagement depMngt = depManager != null ? depManager.manageDependency( dependency ) : null;
-
-            int managedBits = 0;
-            String premanagedVersion = null;
-            String premanagedScope = null;
-            Boolean premanagedOptional = null;
-            Collection<Exclusion> premanagedExclusions = null;
-            Map<String, String> premanagedProperties = null;
-
-            if ( depMngt != null )
-            {
-                if ( depMngt.getVersion() != null && !disableVersionManagement )
-                {
-                    Artifact artifact = dependency.getArtifact();
-                    premanagedVersion = artifact.getVersion();
-                    dependency = dependency.setArtifact( artifact.setVersion( depMngt.getVersion() ) );
-                    managedBits |= DependencyNode.MANAGED_VERSION;
-                }
-                if ( depMngt.getProperties() != null )
-                {
-                    Artifact artifact = dependency.getArtifact();
-                    premanagedProperties = artifact.getProperties();
-                    dependency = dependency.setArtifact( artifact.setProperties( depMngt.getProperties() ) );
-                    managedBits |= DependencyNode.MANAGED_PROPERTIES;
-                }
-                if ( depMngt.getScope() != null )
-                {
-                    premanagedScope = dependency.getScope();
-                    dependency = dependency.setScope( depMngt.getScope() );
-                    managedBits |= DependencyNode.MANAGED_SCOPE;
-                }
-                if ( depMngt.getOptional() != null )
-                {
-                    premanagedOptional = dependency.isOptional();
-                    dependency = dependency.setOptional( depMngt.getOptional() );
-                    managedBits |= DependencyNode.MANAGED_OPTIONAL;
-                }
-                if ( depMngt.getExclusions() != null )
-                {
-                    premanagedExclusions = dependency.getExclusions();
-                    dependency = dependency.setExclusions( depMngt.getExclusions() );
-                    managedBits |= DependencyNode.MANAGED_EXCLUSIONS;
-                }
-            }
-            return new PremanagedDependency( premanagedVersion, premanagedScope, premanagedOptional,
-                                             premanagedExclusions, premanagedProperties, managedBits, dependency,
-                                             premanagedState );
-
-        }
-
-        public void applyTo( DefaultDependencyNode child )
-        {
-            child.setManagedBits( managedBits );
-            if ( premanagedState )
-            {
-                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_VERSION, premanagedVersion );
-                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_SCOPE, premanagedScope );
-                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_OPTIONAL, premanagedOptional );
-                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_EXCLUSIONS, premanagedExclusions );
-                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_PROPERTIES, premanagedProperties );
-            }
-        }
-
-    }
-
 }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java
index 41c0126..28ea747 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java
@@ -28,7 +28,7 @@ import org.eclipse.aether.collection.DependencyGraphTransformationContext;
 
 /**
  */
-class DefaultDependencyGraphTransformationContext
+public class DefaultDependencyGraphTransformationContext
     implements DependencyGraphTransformationContext
 {
 
@@ -36,7 +36,7 @@ class DefaultDependencyGraphTransformationContext
 
     private final Map<Object, Object> map;
 
-    DefaultDependencyGraphTransformationContext( RepositorySystemSession session )
+    public DefaultDependencyGraphTransformationContext( RepositorySystemSession session )
     {
         this.session = session;
         this.map = new HashMap<>();
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java
index bfea062..55b6fba 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java
@@ -36,7 +36,7 @@ import org.eclipse.aether.version.VersionConstraint;
 /**
  * @see DefaultDependencyCollector
  */
-final class DefaultVersionFilterContext
+public final class DefaultVersionFilterContext
     implements VersionFilter.VersionFilterContext
 {
     private final RepositorySystemSession session;
@@ -47,7 +47,7 @@ final class DefaultVersionFilterContext
 
     private List<Version> versions;
 
-    DefaultVersionFilterContext( RepositorySystemSession session )
+    public DefaultVersionFilterContext( RepositorySystemSession session )
     {
         this.session = session;
     }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
new file mode 100644
index 0000000..d47a2b9
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
@@ -0,0 +1,29 @@
+package org.eclipse.aether.internal.impl.collect;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.eclipse.aether.impl.DependencyCollector;
+
+/**
+ * Sub-type for actual implementations.
+ */
+public interface DependencyCollectorDelegate extends DependencyCollector
+{
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java
similarity index 95%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java
index a773367..ad87c22 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.bf;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,6 +19,10 @@ package org.eclipse.aether.internal.impl.collect;
  * under the License.
  */
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -30,13 +34,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Queue;
 
-import static java.util.Objects.requireNonNull;
-import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.RepositoryException;
 import org.eclipse.aether.RepositorySystemSession;
@@ -57,9 +54,14 @@ import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.graph.Exclusion;
 import org.eclipse.aether.impl.ArtifactDescriptorReader;
-import org.eclipse.aether.impl.DependencyCollector;
 import org.eclipse.aether.impl.RemoteRepositoryManager;
 import org.eclipse.aether.impl.VersionRangeResolver;
+import org.eclipse.aether.internal.impl.collect.CachingArtifactTypeRegistry;
+import org.eclipse.aether.internal.impl.collect.DataPool;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyGraphTransformationContext;
+import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext;
+import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
 import org.eclipse.aether.repository.ArtifactRepository;
 import org.eclipse.aether.repository.RemoteRepository;
 import org.eclipse.aether.resolution.ArtifactDescriptorException;
@@ -77,13 +79,18 @@ import org.eclipse.aether.version.Version;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static java.util.Objects.requireNonNull;
+import static org.eclipse.aether.internal.impl.collect.bf.BfDependencyCycle.find;
+
 /**
+ * The breadth-first implementation of {@link org.eclipse.aether.impl.DependencyCollector}.
  */
 @Singleton
-@Named
-public class DefaultDependencyCollector
-    implements DependencyCollector, Service
+@Named( BfDependencyCollector.NAME )
+public class BfDependencyCollector
+        implements DependencyCollectorDelegate, Service
 {
+    public static final String NAME = "bf";
 
     private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
 
@@ -93,7 +100,7 @@ public class DefaultDependencyCollector
 
     private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10;
 
-    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyCollector.class );
+    private static final Logger LOGGER = LoggerFactory.getLogger( BfDependencyCollector.class );
 
     private RemoteRepositoryManager remoteRepositoryManager;
 
@@ -101,13 +108,13 @@ public class DefaultDependencyCollector
 
     private VersionRangeResolver versionRangeResolver;
 
-    public DefaultDependencyCollector()
+    public BfDependencyCollector()
     {
         // enables default constructor
     }
 
     @Inject
-    DefaultDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
+    BfDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
                                 ArtifactDescriptorReader artifactDescriptorReader,
                                 VersionRangeResolver versionRangeResolver )
     {
@@ -123,20 +130,20 @@ public class DefaultDependencyCollector
         setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
     }
 
-    public DefaultDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
+    public BfDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
     {
         this.remoteRepositoryManager =
                 requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" );
         return this;
     }
 
-    public DefaultDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
+    public BfDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
     {
         descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" );
         return this;
     }
 
-    public DefaultDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
+    public BfDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
     {
         this.versionRangeResolver =
                 requireNonNull( versionRangeResolver, "version range resolver cannot be null" );
@@ -266,7 +273,7 @@ public class DefaultDependencyCollector
             for ( Dependency dependency : dependencies )
             {
                 args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser,
+                        new BfProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser,
                                 rootVerFilter, repositories, managedDependencies, Collections.singletonList( node ),
                                 dependency ) );
             }
@@ -360,7 +367,7 @@ public class DefaultDependencyCollector
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void processDependency( Args args, Results results, DependencyProcessingContext context,
+    private void processDependency( Args args, Results results, BfProcessingContext context,
                                     List<Artifact> relocations, boolean disableVersionManagement )
     {
 
@@ -472,7 +479,7 @@ public class DefaultDependencyCollector
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void doRecurse( Args args, DependencyProcessingContext parentContext,
+    private void doRecurse( Args args, BfProcessingContext parentContext,
                             ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
     {
         DefaultDependencyCollectionContext context = args.collectionContext;
@@ -507,7 +514,7 @@ public class DefaultDependencyCollector
             for ( Dependency dependency : descriptorResult.getDependencies() )
             {
                 args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( childSelector, childManager, childTraverser, childFilter,
+                        new BfProcessingContext( childSelector, childManager, childTraverser, childFilter,
                                 childRepos, descriptorResult.getManagedDependencies(), parents, dependency ) );
             }
         }
@@ -520,7 +527,7 @@ public class DefaultDependencyCollector
     private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool,
                                                                       ArtifactDescriptorRequest descriptorRequest,
                                                                       RepositorySystemSession session,
-                                                                      DependencyProcessingContext context,
+                                                                      BfProcessingContext context,
                                                                       Results results )
     {
         Object key = pool.toKey( descriptorRequest );
@@ -689,7 +696,7 @@ public class DefaultDependencyCollector
 
         final DataPool pool;
 
-        final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>();
+        final Queue<BfProcessingContext> dependencyProcessingQueue = new ArrayDeque<>();
 
         final DefaultDependencyCollectionContext collectionContext;
 
@@ -768,7 +775,7 @@ public class DefaultDependencyCollector
         {
             if ( maxCycles < 0 || result.getCycles().size() < maxCycles )
             {
-                result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) );
+                result.addCycle( new BfDependencyCycle( nodes, cycleEntry, dependency ) );
             }
         }
 
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java
similarity index 95%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java
index dd1565c..fb370a6 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.bf;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -30,9 +30,9 @@ import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
 
 /**
- * @see DefaultDependencyCollector
+ * @see BfDependencyCollector
  */
-final class DefaultDependencyCycle
+final class BfDependencyCycle
     implements DependencyCycle
 {
 
@@ -40,7 +40,7 @@ final class DefaultDependencyCycle
 
     private final int cycleEntry;
 
-    DefaultDependencyCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
+    BfDependencyCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
     {
         // skip root node unless it actually has a dependency or is considered the cycle entry (due to its label)
         int offset = ( cycleEntry > 0 && nodes.get( 0 ).getDependency() == null ) ? 1 : 0;
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyProcessingContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java
similarity index 88%
rename from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyProcessingContext.java
rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java
index 71815d0..2cca571 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyProcessingContext.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.bf;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -29,7 +29,7 @@ import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.repository.RemoteRepository;
 
-final class DependencyProcessingContext
+final class BfProcessingContext
 {
     final DependencySelector depSelector;
     final DependencyManager depManager;
@@ -45,7 +45,7 @@ final class DependencyProcessingContext
     final Dependency dependency;
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    DependencyProcessingContext( DependencySelector depSelector,
+    BfProcessingContext( DependencySelector depSelector,
                                  DependencyManager depManager,
                                  DependencyTraverser depTraverser,
                                  VersionFilter verFilter,
@@ -64,9 +64,9 @@ final class DependencyProcessingContext
         this.parents = parents;
     }
 
-    DependencyProcessingContext withDependency( Dependency dependency )
+    BfProcessingContext withDependency( Dependency dependency )
     {
-        return new DependencyProcessingContext( depSelector, depManager, depTraverser, verFilter, repositories,
+        return new BfProcessingContext( depSelector, depManager, depTraverser, verFilter, repositories,
                 managedDependencies, parents, dependency );
     }
 
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
similarity index 80%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
index a773367..1fb00f4 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.df;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,7 +19,10 @@ package org.eclipse.aether.internal.impl.collect;
  * under the License.
  */
 
-import java.util.ArrayDeque;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -28,14 +31,6 @@ import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Queue;
-
-import static java.util.Objects.requireNonNull;
-import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
 
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.RepositoryException;
@@ -57,9 +52,14 @@ import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.graph.Exclusion;
 import org.eclipse.aether.impl.ArtifactDescriptorReader;
-import org.eclipse.aether.impl.DependencyCollector;
 import org.eclipse.aether.impl.RemoteRepositoryManager;
 import org.eclipse.aether.impl.VersionRangeResolver;
+import org.eclipse.aether.internal.impl.collect.CachingArtifactTypeRegistry;
+import org.eclipse.aether.internal.impl.collect.DataPool;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyGraphTransformationContext;
+import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext;
+import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
 import org.eclipse.aether.repository.ArtifactRepository;
 import org.eclipse.aether.repository.RemoteRepository;
 import org.eclipse.aether.resolution.ArtifactDescriptorException;
@@ -77,13 +77,17 @@ import org.eclipse.aether.version.Version;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static java.util.Objects.requireNonNull;
+
 /**
+ * The depth-first implementation of {@link org.eclipse.aether.impl.DependencyCollector}.
  */
 @Singleton
-@Named
-public class DefaultDependencyCollector
-    implements DependencyCollector, Service
+@Named( DfDependencyCollector.NAME )
+public class DfDependencyCollector
+        implements DependencyCollectorDelegate, Service
 {
+    public static final String NAME = "df";
 
     private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
 
@@ -93,7 +97,7 @@ public class DefaultDependencyCollector
 
     private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10;
 
-    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyCollector.class );
+    private static final Logger LOGGER = LoggerFactory.getLogger( DfDependencyCollector.class );
 
     private RemoteRepositoryManager remoteRepositoryManager;
 
@@ -101,15 +105,15 @@ public class DefaultDependencyCollector
 
     private VersionRangeResolver versionRangeResolver;
 
-    public DefaultDependencyCollector()
+    public DfDependencyCollector()
     {
         // enables default constructor
     }
 
     @Inject
-    DefaultDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
-                                ArtifactDescriptorReader artifactDescriptorReader,
-                                VersionRangeResolver versionRangeResolver )
+    DfDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
+                           ArtifactDescriptorReader artifactDescriptorReader,
+                           VersionRangeResolver versionRangeResolver )
     {
         setRemoteRepositoryManager( remoteRepositoryManager );
         setArtifactDescriptorReader( artifactDescriptorReader );
@@ -123,20 +127,20 @@ public class DefaultDependencyCollector
         setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
     }
 
-    public DefaultDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
+    public DfDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
     {
         this.remoteRepositoryManager =
                 requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" );
         return this;
     }
 
-    public DefaultDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
+    public DfDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
     {
         descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" );
         return this;
     }
 
-    public DefaultDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
+    public DfDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
     {
         this.versionRangeResolver =
                 requireNonNull( versionRangeResolver, "version range resolver cannot be null" );
@@ -248,34 +252,23 @@ public class DefaultDependencyCollector
         {
             DataPool pool = new DataPool( session );
 
+            NodeStack
+                    nodes = new NodeStack();
+            nodes.push( node );
+
             DefaultDependencyCollectionContext context =
                 new DefaultDependencyCollectionContext( session, request.getRootArtifact(), root, managedDependencies );
 
             DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext( session );
 
-            Args args = new Args( session, trace, pool, context, versionContext, request );
+            Args args = new Args( session, trace, pool, nodes, context, versionContext, request );
             Results results = new Results( result, session );
 
-            DependencySelector rootDepSelector =
-                    depSelector != null ? depSelector.deriveChildSelector( context ) : null;
-            DependencyManager rootDepManager = depManager != null ? depManager.deriveChildManager( context ) : null;
-            DependencyTraverser rootDepTraverser =
-                    depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null;
-            VersionFilter rootVerFilter = verFilter != null ? verFilter.deriveChildFilter( context ) : null;
-
-            for ( Dependency dependency : dependencies )
-            {
-                args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser,
-                                rootVerFilter, repositories, managedDependencies, Collections.singletonList( node ),
-                                dependency ) );
-            }
-
-            while ( !args.dependencyProcessingQueue.isEmpty() )
-            {
-                processDependency( args, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(),
-                        false );
-            }
+            process( args, results, dependencies, repositories,
+                     depSelector != null ? depSelector.deriveChildSelector( context ) : null,
+                     depManager != null ? depManager.deriveChildManager( context ) : null,
+                     depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null,
+                     verFilter != null ? verFilter.deriveChildFilter( context ) : null );
 
             errorPath = results.errorPath;
         }
@@ -360,39 +353,61 @@ public class DefaultDependencyCollector
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void processDependency( Args args, Results results, DependencyProcessingContext context,
+    private void process( final Args args, Results results, List<Dependency> dependencies,
+                          List<RemoteRepository> repositories, DependencySelector depSelector,
+                          DependencyManager depManager, DependencyTraverser depTraverser, VersionFilter verFilter )
+    {
+        for ( Dependency dependency : dependencies )
+        {
+            processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter,
+                               dependency );
+        }
+    }
+
+    @SuppressWarnings( "checkstyle:parameternumber" )
+    private void processDependency( Args args, Results results, List<RemoteRepository> repositories,
+                                    DependencySelector depSelector, DependencyManager depManager,
+                                    DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency )
+    {
+
+        List<Artifact> relocations = Collections.emptyList();
+        processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, dependency,
+                           relocations, false );
+    }
+
+    @SuppressWarnings( "checkstyle:parameternumber" )
+    private void processDependency( Args args, Results results, List<RemoteRepository> repositories,
+                                    DependencySelector depSelector, DependencyManager depManager,
+                                    DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency,
                                     List<Artifact> relocations, boolean disableVersionManagement )
     {
 
-        if ( context.depSelector != null && !context.depSelector.selectDependency( context.dependency ) )
+        if ( depSelector != null && !depSelector.selectDependency( dependency ) )
         {
             return;
         }
 
         PremanagedDependency preManaged =
-                PremanagedDependency.create( context.depManager, context.dependency, disableVersionManagement,
-                        args.premanagedState );
-        Dependency dependency = preManaged.managedDependency;
+            PremanagedDependency.create( depManager, dependency, disableVersionManagement, args.premanagedState );
+        dependency = preManaged.managedDependency;
 
         boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() );
 
-        boolean traverse =
-                !noDescriptor && ( context.depTraverser == null || context.depTraverser.traverseDependency(
-                        dependency ) );
+        boolean traverse = !noDescriptor && ( depTraverser == null || depTraverser.traverseDependency( dependency ) );
 
         List<? extends Version> versions;
         VersionRangeResult rangeResult;
         try
         {
-            VersionRangeRequest rangeRequest = createVersionRangeRequest( args, context.repositories, dependency );
+            VersionRangeRequest rangeRequest = createVersionRangeRequest( args, repositories, dependency );
 
             rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
 
-            versions = filterVersions( dependency, rangeResult, context.verFilter, args.versionContext );
+            versions = filterVersions( dependency, rangeResult, verFilter, args.versionContext );
         }
         catch ( VersionRangeResolutionException e )
         {
-            results.addException( dependency, e, context.parents );
+            results.addException( dependency, e, args.nodes );
             return;
         }
 
@@ -401,30 +416,27 @@ public class DefaultDependencyCollector
             Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() );
             Dependency d = dependency.setArtifact( originalArtifact );
 
-            ArtifactDescriptorRequest descriptorRequest =
-                    createArtifactDescriptorRequest( args, context.repositories, d );
+            ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest( args, repositories, d );
 
             final ArtifactDescriptorResult descriptorResult =
-                    noDescriptor
-                            ? new ArtifactDescriptorResult( descriptorRequest )
-                            : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session,
-                                    context.withDependency( d ), results );
-
+                getArtifactDescriptorResult( args, results, noDescriptor, d, descriptorRequest );
             if ( descriptorResult != null )
             {
                 d = d.setArtifact( descriptorResult.getArtifact() );
 
-                int cycleEntry = find( context.parents, d.getArtifact() );
+                DependencyNode node = args.nodes.top();
+
+                int cycleEntry = args.nodes.find( d.getArtifact() );
                 if ( cycleEntry >= 0 )
                 {
-                    results.addCycle( context.parents, cycleEntry, d );
-                    DependencyNode cycleNode = context.parents.get( cycleEntry );
+                    results.addCycle( args.nodes, cycleEntry, d );
+                    DependencyNode cycleNode = args.nodes.get( cycleEntry );
                     if ( cycleNode.getDependency() != null )
                     {
                         DefaultDependencyNode child =
-                                createDependencyNode( relocations, preManaged, rangeResult, version, d,
-                                        descriptorResult, cycleNode );
-                        context.getParent().getChildren().add( child );
+                            createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult,
+                                                  cycleNode );
+                        node.getChildren().add( child );
                         continue;
                     }
                 }
@@ -435,8 +447,8 @@ public class DefaultDependencyCollector
                         originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
                             && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
 
-                    processDependency( args, results, context.withDependency( d ), descriptorResult.getRelocations(),
-                            disableVersionManagementSubsequently );
+                    processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
+                                       descriptorResult.getRelocations(), disableVersionManagementSubsequently );
                     return;
                 }
                 else
@@ -444,72 +456,69 @@ public class DefaultDependencyCollector
                     d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) );
 
                     List<RemoteRepository> repos =
-                        getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
+                        getRemoteRepositories( rangeResult.getRepository( version ), repositories );
 
                     DefaultDependencyNode child =
                         createDependencyNode( relocations, preManaged, rangeResult, version, d,
                                               descriptorResult.getAliases(), repos, args.request.getRequestContext() );
 
-                    context.getParent().getChildren().add( child );
+                    node.getChildren().add( child );
 
                     boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
                     if ( recurse )
                     {
-                        doRecurse( args, context.withDependency( d ), descriptorResult, child );
+                        doRecurse( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
+                                   descriptorResult, child );
                     }
                 }
             }
             else
             {
+                DependencyNode node = args.nodes.top();
                 List<RemoteRepository> repos =
-                    getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
+                    getRemoteRepositories( rangeResult.getRepository( version ), repositories );
                 DefaultDependencyNode child =
                     createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos,
                                           args.request.getRequestContext() );
-                context.getParent().getChildren().add( child );
+                node.getChildren().add( child );
             }
         }
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void doRecurse( Args args, DependencyProcessingContext parentContext,
+    private void doRecurse( Args args, Results results, List<RemoteRepository> repositories,
+                            DependencySelector depSelector, DependencyManager depManager,
+                            DependencyTraverser depTraverser, VersionFilter verFilter, Dependency d,
                             ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
     {
         DefaultDependencyCollectionContext context = args.collectionContext;
-        context.set( parentContext.dependency, descriptorResult.getManagedDependencies() );
+        context.set( d, descriptorResult.getManagedDependencies() );
 
-        DependencySelector childSelector =
-                parentContext.depSelector != null ? parentContext.depSelector.deriveChildSelector( context ) : null;
-        DependencyManager childManager =
-                parentContext.depManager != null ? parentContext.depManager.deriveChildManager( context ) : null;
-        DependencyTraverser childTraverser =
-                parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser( context ) : null;
-        VersionFilter childFilter =
-                parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter( context ) : null;
+        DependencySelector childSelector = depSelector != null ? depSelector.deriveChildSelector( context ) : null;
+        DependencyManager childManager = depManager != null ? depManager.deriveChildManager( context ) : null;
+        DependencyTraverser childTraverser = depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null;
+        VersionFilter childFilter = verFilter != null ? verFilter.deriveChildFilter( context ) : null;
 
         final List<RemoteRepository> childRepos =
-                args.ignoreRepos
-                        ? parentContext.repositories
-                        : remoteRepositoryManager.aggregateRepositories( args.session, parentContext.repositories,
-                        descriptorResult.getRepositories(), true );
+            args.ignoreRepos
+                ? repositories
+                : remoteRepositoryManager.aggregateRepositories( args.session, repositories,
+                                                                 descriptorResult.getRepositories(), true );
 
         Object key =
-                args.pool.toKey( parentContext.dependency.getArtifact(), childRepos, childSelector, childManager,
-                        childTraverser, childFilter );
+            args.pool.toKey( d.getArtifact(), childRepos, childSelector, childManager, childTraverser, childFilter );
 
         List<DependencyNode> children = args.pool.getChildren( key );
         if ( children == null )
         {
             args.pool.putChildren( key, child.getChildren() );
 
-            List<DependencyNode> parents = new ArrayList<>( parentContext.parents );
-            parents.add( child );
-            for ( Dependency dependency : descriptorResult.getDependencies() )
-            {
-                args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( childSelector, childManager, childTraverser, childFilter,
-                                childRepos, descriptorResult.getManagedDependencies(), parents, dependency ) );
-            }
+            args.nodes.push( child );
+
+            process( args, results, descriptorResult.getDependencies(), childRepos, childSelector, childManager,
+                     childTraverser, childFilter );
+
+            args.nodes.pop();
         }
         else
         {
@@ -517,11 +526,20 @@ public class DefaultDependencyCollector
         }
     }
 
+    private ArtifactDescriptorResult getArtifactDescriptorResult( Args args, Results results, boolean noDescriptor,
+                                                                  Dependency d,
+                                                                  ArtifactDescriptorRequest descriptorRequest )
+    {
+        return noDescriptor
+                   ? new ArtifactDescriptorResult( descriptorRequest )
+                   : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session, d, results, args );
+
+    }
+
     private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool,
                                                                       ArtifactDescriptorRequest descriptorRequest,
-                                                                      RepositorySystemSession session,
-                                                                      DependencyProcessingContext context,
-                                                                      Results results )
+                                                                      RepositorySystemSession session, Dependency d,
+                                                                      Results results, Args args )
     {
         Object key = pool.toKey( descriptorRequest );
         ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest );
@@ -534,7 +552,7 @@ public class DefaultDependencyCollector
             }
             catch ( ArtifactDescriptorException e )
             {
-                results.addException( context.dependency, e, context.parents );
+                results.addException( d, e, args.nodes );
                 pool.putDescriptor( key, e );
                 return null;
             }
@@ -689,7 +707,7 @@ public class DefaultDependencyCollector
 
         final DataPool pool;
 
-        final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>();
+        final NodeStack nodes;
 
         final DefaultDependencyCollectionContext collectionContext;
 
@@ -697,7 +715,7 @@ public class DefaultDependencyCollector
 
         final CollectRequest request;
 
-        Args( RepositorySystemSession session, RequestTrace trace, DataPool pool,
+        Args( RepositorySystemSession session, RequestTrace trace, DataPool pool, NodeStack nodes,
                      DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext,
                      CollectRequest request )
         {
@@ -707,6 +725,7 @@ public class DefaultDependencyCollector
             this.premanagedState = ConfigUtils.getBoolean( session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE );
             this.trace = trace;
             this.pool = pool;
+            this.nodes = nodes;
             this.collectionContext = collectionContext;
             this.versionContext = versionContext;
         }
@@ -734,7 +753,7 @@ public class DefaultDependencyCollector
             maxCycles = ConfigUtils.getInteger( session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES );
         }
 
-        public void addException( Dependency dependency, Exception e, List<DependencyNode> nodes )
+        public void addException( Dependency dependency, Exception e, NodeStack nodes )
         {
             if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions )
             {
@@ -742,13 +761,13 @@ public class DefaultDependencyCollector
                 if ( errorPath == null )
                 {
                     StringBuilder buffer = new StringBuilder( 256 );
-                    for ( DependencyNode node : nodes )
+                    for ( int i = 0; i < nodes.size(); i++ )
                     {
                         if ( buffer.length() > 0 )
                         {
                             buffer.append( " -> " );
                         }
-                        Dependency dep = node.getDependency();
+                        Dependency dep = nodes.get( i ).getDependency();
                         if ( dep != null )
                         {
                             buffer.append( dep.getArtifact() );
@@ -764,11 +783,11 @@ public class DefaultDependencyCollector
             }
         }
 
-        public void addCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
+        public void addCycle( NodeStack nodes, int cycleEntry, Dependency dependency )
         {
             if ( maxCycles < 0 || result.getCycles().size() < maxCycles )
             {
-                result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) );
+                result.addCycle( new DfDependencyCycle( nodes, cycleEntry, dependency ) );
             }
         }
 
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java
similarity index 57%
rename from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java
rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java
index dd1565c..b0ff519 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.df;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -23,16 +23,15 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyCycle;
 import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
 
 /**
- * @see DefaultDependencyCollector
+ * @see DfDependencyCollector
  */
-final class DefaultDependencyCycle
+final class DfDependencyCycle
     implements DependencyCycle
 {
 
@@ -40,7 +39,7 @@ final class DefaultDependencyCycle
 
     private final int cycleEntry;
 
-    DefaultDependencyCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
+    DfDependencyCycle( NodeStack nodes, int cycleEntry, Dependency dependency )
     {
         // skip root node unless it actually has a dependency or is considered the cycle entry (due to its label)
         int offset = ( cycleEntry > 0 && nodes.get( 0 ).getDependency() == null ) ? 1 : 0;
@@ -70,58 +69,6 @@ final class DefaultDependencyCycle
         return dependencies.subList( cycleEntry, dependencies.size() );
     }
 
-    /**
-     * Searches for a node associated with the given artifact. A version of the artifact is not considered during the
-     * search.
-     *
-     * @param nodes a list representing single path in the dependency graph. First element is the root.
-     * @param artifact to find among the parent nodes.
-     * @return the index of the node furthest from the root and associated with the given artifact, or {@literal -1} if
-     * there is no such node.
-     */
-    public static int find( List<DependencyNode> nodes, Artifact artifact )
-    {
-
-        for ( int i = nodes.size() - 1; i >= 0; i-- )
-        {
-            DependencyNode node = nodes.get( i );
-
-            Artifact a = node.getArtifact();
-            if ( a == null )
-            {
-                break;
-            }
-
-            if ( !a.getArtifactId().equals( artifact.getArtifactId() ) )
-            {
-                continue;
-            }
-            if ( !a.getGroupId().equals( artifact.getGroupId() ) )
-            {
-                continue;
-            }
-            if ( !a.getExtension().equals( artifact.getExtension() ) )
-            {
-                continue;
-            }
-            if ( !a.getClassifier().equals( artifact.getClassifier() ) )
-            {
-                continue;
-            }
-            /*
-             * NOTE: While a:1 and a:2 are technically different artifacts, we want to consider the path a:2 -> b:2 ->
-             * a:1 a cycle in the current context. The artifacts themselves might not form a cycle but their producing
-             * projects surely do. Furthermore, conflict resolution will always have to consider a:1 a loser (otherwise
-             * its ancestor a:2 would get pruned and so would a:1) so there is no point in building the sub graph of
-             * a:1.
-             */
-
-            return i;
-        }
-
-        return -1;
-    }
-
     @Override
     public String toString()
     {
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java
new file mode 100644
index 0000000..81123c1
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java
@@ -0,0 +1,127 @@
+package org.eclipse.aether.internal.impl.collect.df;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Arrays;
+
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.graph.DependencyNode;
+
+/**
+ * @see DfDependencyCollector
+ */
+final class NodeStack
+{
+
+    @SuppressWarnings( {"unchecked", "checkstyle:magicnumber" } )
+    // CHECKSTYLE_OFF: MagicNumber
+    private DependencyNode[] nodes = new DependencyNode[96];
+    // CHECKSTYLE_ON: MagicNumber
+
+    private int size;
+
+    public DependencyNode top()
+    {
+        if ( size <= 0 )
+        {
+            throw new IllegalStateException( "stack empty" );
+        }
+        return nodes[size - 1];
+    }
+
+    public void push( DependencyNode node )
+    {
+        if ( size >= nodes.length )
+        {
+            DependencyNode[] tmp = new DependencyNode[size + 64];
+            System.arraycopy( nodes, 0, tmp, 0, nodes.length );
+            nodes = tmp;
+        }
+        nodes[size++] = node;
+    }
+
+    public void pop()
+    {
+        if ( size <= 0 )
+        {
+            throw new IllegalStateException( "stack empty" );
+        }
+        size--;
+    }
+
+    public int find( Artifact artifact )
+    {
+        for ( int i = size - 1; i >= 0; i-- )
+        {
+            DependencyNode node = nodes[i];
+
+            Artifact a = node.getArtifact();
+            if ( a == null )
+            {
+                break;
+            }
+
+            if ( !a.getArtifactId().equals( artifact.getArtifactId() ) )
+            {
+                continue;
+            }
+            if ( !a.getGroupId().equals( artifact.getGroupId() ) )
+            {
+                continue;
+            }
+            if ( !a.getExtension().equals( artifact.getExtension() ) )
+            {
+                continue;
+            }
+            if ( !a.getClassifier().equals( artifact.getClassifier() ) )
+            {
+                continue;
+            }
+            /*
+             * NOTE: While a:1 and a:2 are technically different artifacts, we want to consider the path a:2 -> b:2 ->
+             * a:1 a cycle in the current context. The artifacts themselves might not form a cycle but their producing
+             * projects surely do. Furthermore, conflict resolution will always have to consider a:1 a loser (otherwise
+             * its ancestor a:2 would get pruned and so would a:1) so there is no point in building the sub graph of
+             * a:1.
+             */
+
+            return i;
+        }
+
+        return -1;
+    }
+
+    public int size()
+    {
+        return size;
+    }
+
+    public DependencyNode get( int index )
+    {
+        return nodes[index];
+    }
+
+    @Override
+    public String toString()
+    {
+        return Arrays.toString( nodes );
+    }
+
+}
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java
similarity index 99%
copy from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java
copy to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java
index 5d4c2aa..fcae807 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.bf;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -18,14 +18,6 @@ package org.eclipse.aether.internal.impl.collect;
  * specific language governing permissions and limitations
  * under the License.
  */
-import static java.util.Objects.requireNonNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -73,12 +65,21 @@ import org.eclipse.aether.util.graph.version.HighestVersionFilter;
 import org.junit.Before;
 import org.junit.Test;
 
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 /**
  */
-public class DefaultDependencyCollectorTest
+public class BfDependencyCollectorTest
 {
 
-    private DefaultDependencyCollector collector;
+    private BfDependencyCollector collector;
 
     private DefaultRepositorySystemSession session;
 
@@ -106,7 +107,7 @@ public class DefaultDependencyCollectorTest
     {
         session = TestUtils.newSession();
 
-        collector = new DefaultDependencyCollector();
+        collector = new BfDependencyCollector();
         collector.setArtifactDescriptorReader( newReader( "" ) );
         collector.setVersionRangeResolver( new StubVersionRangeResolver() );
         collector.setRemoteRepositoryManager( new StubRemoteRepositoryManager() );
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java
similarity index 90%
copy from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java
copy to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java
index 0a382f8..a1be7c9 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.bf;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,6 +19,9 @@ package org.eclipse.aether.internal.impl.collect;
  * under the License.
  */
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.eclipse.aether.artifact.DefaultArtifact;
 import org.eclipse.aether.graph.DefaultDependencyNode;
 import org.eclipse.aether.graph.Dependency;
@@ -26,12 +29,9 @@ import org.eclipse.aether.graph.DependencyCycle;
 import org.eclipse.aether.graph.DependencyNode;
 import org.junit.Test;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import static org.junit.Assert.assertEquals;
 
-public class DefaultDependencyCycleTest
+public class BfDependencyCycleTest
 {
     private static final Dependency FOO_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:foo:1.0" ), "test" );
     private static final Dependency BAR_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:bar:1.0" ), "test" );
@@ -41,7 +41,7 @@ public class DefaultDependencyCycleTest
     {
         List<DependencyNode> nodes = new ArrayList<>();
         nodes.add( new DefaultDependencyNode( FOO_DEPENDENCY ) );
-        DependencyCycle cycle = new DefaultDependencyCycle( nodes, 1, BAR_DEPENDENCY );
+        DependencyCycle cycle = new BfDependencyCycle( nodes, 1, BAR_DEPENDENCY );
 
         assertEquals( "group-id:foo:jar -> group-id:bar:jar", cycle.toString() );
     }
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollectorTest.java
similarity index 99%
rename from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java
rename to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollectorTest.java
index 5d4c2aa..61d6e09 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollectorTest.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.df;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -18,14 +18,6 @@ package org.eclipse.aether.internal.impl.collect;
  * specific language governing permissions and limitations
  * under the License.
  */
-import static java.util.Objects.requireNonNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -58,6 +50,7 @@ import org.eclipse.aether.impl.ArtifactDescriptorReader;
 import org.eclipse.aether.internal.impl.IniArtifactDescriptorReader;
 import org.eclipse.aether.internal.impl.StubRemoteRepositoryManager;
 import org.eclipse.aether.internal.impl.StubVersionRangeResolver;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollector;
 import org.eclipse.aether.internal.test.util.DependencyGraphParser;
 import org.eclipse.aether.internal.test.util.TestUtils;
 import org.eclipse.aether.repository.RemoteRepository;
@@ -73,12 +66,21 @@ import org.eclipse.aether.util.graph.version.HighestVersionFilter;
 import org.junit.Before;
 import org.junit.Test;
 
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 /**
  */
-public class DefaultDependencyCollectorTest
+public class DfDependencyCollectorTest
 {
 
-    private DefaultDependencyCollector collector;
+    private DfDependencyCollector collector;
 
     private DefaultRepositorySystemSession session;
 
@@ -106,7 +108,7 @@ public class DefaultDependencyCollectorTest
     {
         session = TestUtils.newSession();
 
-        collector = new DefaultDependencyCollector();
+        collector = new DfDependencyCollector();
         collector.setArtifactDescriptorReader( newReader( "" ) );
         collector.setVersionRangeResolver( new StubVersionRangeResolver() );
         collector.setRemoteRepositoryManager( new StubRemoteRepositoryManager() );
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java
similarity index 78%
rename from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java
rename to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java
index 0a382f8..0cc8a7d 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.df;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -23,15 +23,11 @@ import org.eclipse.aether.artifact.DefaultArtifact;
 import org.eclipse.aether.graph.DefaultDependencyNode;
 import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyCycle;
-import org.eclipse.aether.graph.DependencyNode;
 import org.junit.Test;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import static org.junit.Assert.assertEquals;
 
-public class DefaultDependencyCycleTest
+public class DfDependencyCycleTest
 {
     private static final Dependency FOO_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:foo:1.0" ), "test" );
     private static final Dependency BAR_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:bar:1.0" ), "test" );
@@ -39,9 +35,10 @@ public class DefaultDependencyCycleTest
     @Test
     public void testToString()
     {
-        List<DependencyNode> nodes = new ArrayList<>();
-        nodes.add( new DefaultDependencyNode( FOO_DEPENDENCY ) );
-        DependencyCycle cycle = new DefaultDependencyCycle( nodes, 1, BAR_DEPENDENCY );
+        org.eclipse.aether.internal.impl.collect.df.NodeStack
+                nodeStack = new org.eclipse.aether.internal.impl.collect.df.NodeStack();
+        nodeStack.push( new DefaultDependencyNode( FOO_DEPENDENCY ) );
+        DependencyCycle cycle = new DfDependencyCycle( nodeStack, 1, BAR_DEPENDENCY );
 
         assertEquals( "group-id:foo:jar -> group-id:bar:jar", cycle.toString() );
     }