You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by rf...@apache.org on 2019/11/10 14:05:24 UTC

[maven-artifact-transfer] branch MSHARED-801 created (now c3edb95)

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

rfscholte pushed a change to branch MSHARED-801
in repository https://gitbox.apache.org/repos/asf/maven-artifact-transfer.git.


      at c3edb95  [MSHARED-801] Add functionality to collect raw dependencies in Maven 3+

This branch includes the following new commits:

     new e51ebb5  Add functionality to collect raw dependencies in Maven 3+
     new c3edb95  [MSHARED-801] Add functionality to collect raw dependencies in Maven 3+

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[maven-artifact-transfer] 01/02: Add functionality to collect raw dependencies in Maven 3+

Posted by rf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rfscholte pushed a commit to branch MSHARED-801
in repository https://gitbox.apache.org/repos/asf/maven-artifact-transfer.git

commit e51ebb5b20bb696417b92f9fa523d3c120fba0c1
Author: Gabriel Belingueres <be...@gmail.com>
AuthorDate: Sat Feb 9 00:08:47 2019 -0300

    Add functionality to collect raw dependencies in Maven 3+
    
    - Added functionality to collect the node tree of project dependencies.
    - Adapted some Eclipse Aether classes to work with Sonatype Aether
    classes.
    - Implemented a collector plugin using the new functionality to run ITs.
---
 pom.xml                                            |    6 +
 src/it/maven-dependency-collector-plugin/pom.xml   |  130 ++
 .../collector/DependencyCollectorMojo.java         |  110 ++
 .../collector/DependencyCollectorTest.java         |  415 ++++++
 .../src/test/projects/managedDepsAndScope/pom.xml  |   68 +
 .../src/test/projects/mockDependencies/a10/pom.xml |   34 +
 .../src/test/projects/mockDependencies/b10/pom.xml |   33 +
 .../src/test/projects/mockDependencies/c10/pom.xml |   33 +
 .../src/test/projects/mockDependencies/d10/pom.xml |   34 +
 .../src/test/projects/mockDependencies/pom.xml     |   37 +
 .../src/test/projects/mockDependencies/t10/pom.xml |   25 +
 .../src/test/projects/mockDependencies/t11/pom.xml |   33 +
 .../src/test/projects/mockDependencies/u10/pom.xml |   25 +
 .../src/test/projects/mockDependencies/u11/pom.xml |   25 +
 .../src/test/projects/noExcludedDep/pom.xml        |   68 +
 .../src/test/projects/noRepeatedDeps/pom.xml       |   62 +
 .../test/projects/noTransitiveOptionalDep/pom.xml  |   57 +
 .../src/test/projects/noTransitiveTestDep/pom.xml  |   63 +
 .../src/test/projects/optionalDep/pom.xml          |   59 +
 .../src/test/projects/transitiveTestDeps/pom.xml   |   58 +
 .../src/test/projects/versionConstraintDep/pom.xml |   57 +
 .../dependencies/collect/CollectorResult.java      |    6 +
 .../dependencies/collect/DependencyCollector.java  |   10 +
 .../internal/DefaultDependencyCollector.java       |   16 +
 .../collect/internal/Maven30CollectorResult.java   |   85 ++
 .../collect/internal/Maven30ConfigUtils.java       |  342 +++++
 .../collect/internal/Maven30ConflictIdSorter.java  |  369 ++++++
 .../collect/internal/Maven30ConflictResolver.java  | 1345 ++++++++++++++++++++
 .../internal/Maven30DependencyCollector.java       |  102 +-
 .../Maven30DirectScopeDependencySelector.java      |  130 ++
 .../collect/internal/Maven30JavaScopeDeriver.java  |   71 ++
 .../collect/internal/Maven30JavaScopeSelector.java |  100 ++
 .../internal/Maven30NearestVersionSelector.java    |  181 +++
 .../collect/internal/Maven30NodeData.java          |   91 ++
 .../Maven30PathRecordingDependencyVisitor.java     |  125 ++
 .../internal/Maven30SimpleOptionalitySelector.java |   63 +
 .../collect/internal/Maven30Stack.java             |   89 ++
 .../collect/internal/Maven31CollectorResult.java   |   86 ++
 .../internal/Maven31DependencyCollector.java       |  116 ++
 .../Maven31DirectScopeDependencySelector.java      |  131 ++
 .../collect/internal/MavenDependencyCollector.java |    4 +
 41 files changed, 4893 insertions(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 748c00e..1b789d5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -141,6 +141,12 @@
     </dependency>
 
     <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-dependency-tree</artifactId>
+      <version>3.0.1</version>
+    </dependency>
+
+    <dependency>
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-component-annotations</artifactId>
     </dependency>
diff --git a/src/it/maven-dependency-collector-plugin/pom.xml b/src/it/maven-dependency-collector-plugin/pom.xml
new file mode 100644
index 0000000..78ad7e1
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/pom.xml
@@ -0,0 +1,130 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+>
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven.plugins</groupId>
+    <artifactId>maven-plugins</artifactId>
+    <version>31</version>
+    <relativePath />
+  </parent>
+
+  <artifactId>maven-dependency-collector-plugin</artifactId>
+  <version>1.0.0</version>
+  <packaging>maven-plugin</packaging>
+
+  <name>Apache Maven Dependency Collector Plugin</name>
+  <description>The plugin is only intended as a real testing environment for parts 
+    of the maven-artifact-transfer component. In this test we check the DependencyCollector</description>
+  <prerequisites>
+    <maven>${mavenVersion}</maven>
+  </prerequisites>
+
+  <properties>
+    <surefire.version>2.21.0</surefire.version>
+    <mavenVersion>3.0</mavenVersion>
+    <javaVersion>8</javaVersion>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>${mavenVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-artifact</artifactId>
+      <version>${mavenVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-artifact-transfer</artifactId>
+      <version>@project.version@</version>
+    </dependency>
+
+    <!-- dependencies to annotations -->
+    <dependency>
+      <groupId>org.apache.maven.plugin-tools</groupId>
+      <artifactId>maven-plugin-annotations</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.takari.maven.plugins</groupId>
+      <artifactId>takari-plugin-integration-testing</artifactId>
+      <version>2.9.2</version>
+      <type>pom</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.takari.maven.plugins</groupId>
+      <artifactId>takari-plugin-testing</artifactId>
+      <version>2.9.2</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.rat</groupId>
+          <artifactId>apache-rat-plugin</artifactId>
+          <configuration>
+            <skip>true</skip>
+          </configuration>
+        </plugin>
+        <plugin>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <configuration>
+            <systemProperties>
+              <maven.local.repo>${maven.local.repo}</maven.local.repo>
+              <localRepositoryPath>${localRepositoryPath}</localRepositoryPath>
+              <mvnVersion>${mvnVersion}</mvnVersion>
+            </systemProperties>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+    <plugins>
+      <plugin>
+        <groupId>io.takari.maven.plugins</groupId>
+        <artifactId>takari-lifecycle-plugin</artifactId>
+        <version>1.13.4</version>
+        <executions>
+          <execution>
+            <?m2e ignore ?>
+            <id>testProperties</id>
+            <phase>process-test-resources</phase>
+            <goals>
+              <goal>testProperties</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+
+  </build>
+</project>
diff --git a/src/it/maven-dependency-collector-plugin/src/main/java/org/apache/maven/plugin/dependency/collector/DependencyCollectorMojo.java b/src/it/maven-dependency-collector-plugin/src/main/java/org/apache/maven/plugin/dependency/collector/DependencyCollectorMojo.java
new file mode 100644
index 0000000..45ab470
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/main/java/org/apache/maven/plugin/dependency/collector/DependencyCollectorMojo.java
@@ -0,0 +1,110 @@
+package org.apache.maven.plugin.dependency.collector;
+
+/*
+ * 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.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.DefaultProjectBuildingRequest;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.shared.dependency.graph.DependencyNode;
+import org.apache.maven.shared.dependency.graph.traversal.SerializingDependencyNodeVisitor;
+import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult;
+import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollector;
+import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollectorException;
+import org.apache.maven.shared.transfer.project.NoFileAssignedException;
+import org.apache.maven.shared.transfer.project.deploy.ProjectDeployer;
+import org.apache.maven.shared.transfer.project.deploy.ProjectDeployerRequest;
+import org.apache.maven.shared.transfer.project.install.ProjectInstaller;
+
+/**
+ * This mojo is implemented to test the {@link DependencyCollector} part of the maven-artifact-transfer shared component.
+ * 
+ * @author Gabriel Belingueres
+ */
+@Mojo( name = "dependency-collector", defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true )
+public class DependencyCollectorMojo
+    extends AbstractMojo
+{
+
+    /**
+     * Parameter to have different locations for each Maven version we are testing with.
+     */
+    @Parameter
+    private String mvnVersion;
+
+    @Parameter( defaultValue = "${session}", required = true, readonly = true )
+    protected MavenSession session;
+
+    @Parameter( defaultValue = "${project}", required = true, readonly = true )
+    protected MavenProject project;
+
+    @Component
+    private DependencyCollector dependencyCollector;
+
+    public void execute()
+        throws MojoExecutionException, MojoFailureException
+    {
+        getLog().info( "Hello from dependency-collector plugin" );
+
+        ProjectBuildingRequest buildingRequest =
+            new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
+        buildingRequest.setProject( project );
+        
+        collectDependencies( buildingRequest );
+        getLog().info( "Bye bye from dependency-collector plugin" );
+    }
+
+    private void collectDependencies( ProjectBuildingRequest buildingRequest ) throws MojoExecutionException
+    {
+        try
+        {
+            CollectorResult result = dependencyCollector.collectDependenciesGraph( buildingRequest );
+            DependencyNode root = result.getDependencyGraphRoot();
+
+            StringWriter writer = new StringWriter();
+            SerializingDependencyNodeVisitor visitor = new SerializingDependencyNodeVisitor( writer );
+            root.accept( visitor );
+            
+            getLog().info( writer.toString() );
+        }
+        catch ( DependencyCollectorException e )
+        {
+            throw new MojoExecutionException( "DependencyCollectorException", e );
+        }
+    }
+
+}
diff --git a/src/it/maven-dependency-collector-plugin/src/test/java/org/apache/maven/plugin/dependency/collector/DependencyCollectorTest.java b/src/it/maven-dependency-collector-plugin/src/test/java/org/apache/maven/plugin/dependency/collector/DependencyCollectorTest.java
new file mode 100644
index 0000000..9688de5
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/java/org/apache/maven/plugin/dependency/collector/DependencyCollectorTest.java
@@ -0,0 +1,415 @@
+package org.apache.maven.plugin.dependency.collector;
+
+/*
+ * 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 static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.codehaus.plexus.util.FileUtils;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+import io.takari.maven.testing.TestResources;
+import io.takari.maven.testing.executor.MavenExecution;
+import io.takari.maven.testing.executor.MavenExecutionResult;
+import io.takari.maven.testing.executor.MavenRuntime;
+import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder;
+import io.takari.maven.testing.executor.MavenVersions;
+import io.takari.maven.testing.executor.junit.MavenJUnitTestRunner;
+
+/**
+ * This will check if the DependencyCollector works for all Maven versions 3.0.5, 3.1.1, 3.2.5, 3.3.1, 3.3.9, 3.5.0, 3.5.2,
+ * 3.5.3, 3.5.4, 3.6.0. This is done by using the test plugin <code>maven-dependency-collector-plugin</code> which uses the
+ * DependencyCollector as component. By using this way we get a real runtime environment which supports all Maven versions.
+ * 
+ * @author Gabriel Belingueres
+ */
+@RunWith( MavenJUnitTestRunner.class )
+@MavenVersions( {
+    // (Maven version) <-- uses (Aether version)
+    // test ONLY with the most recent Maven versions that make use of an specific Aether version.
+    "3.0",   // <-- Sonatype Aether 1.7
+    "3.0.1", // <-- Sonatype Aether 1.8
+    "3.0.2", // <-- Sonatype Aether 1.9
+    "3.0.3", // <-- Sonatype Aether 1.11
+//    "3.0.4", // <-- Sonatype Aether 1.13.1
+    "3.0.5", // <-- Sonatype Aether 1.13.1
+    
+//    "3.1.0", // <-- Eclipse Aether 0.9.0M2
+    "3.1.1", // <-- Eclipse Aether 0.9.0M2
+    
+//    "3.2.1", // <-- Eclipse Aether 0.9.0M2
+//    "3.2.2", // <-- Eclipse Aether 0.9.0M2
+    "3.2.3", // <-- Eclipse Aether 0.9.0M2
+    "3.2.5", // <-- Eclipse Aether 1.0.0.v20140518
+    
+//    "3.3.1", // <-- Eclipse Aether 1.0.2.v20150114 
+//    "3.3.3", // <-- Eclipse Aether 1.0.2.v20150114 
+    "3.3.9", // <-- Eclipse Aether 1.0.2.v20150114
+    
+    "3.5.0", // <-- Maven Resolver 1.0.3
+    "3.5.2", // <-- Maven Resolver 1.1.0
+//    "3.5.3", // <-- Maven Resolver 1.1.1
+    "3.5.4", // <-- Maven Resolver 1.1.1
+    "3.6.0",  // <-- Maven Resolver 1.3.1
+    "3.6.1",  // <-- Maven Resolver 1.3.3
+    "3.6.2"  // <-- Maven Resolver 1.4.1
+} )
+public class DependencyCollectorTest
+{
+    
+    private static final String LS = System.lineSeparator();
+    
+    private static boolean testDependenciesInstalled = false;
+
+    @Rule
+    public final TestResources resources = new TestResources();
+
+    /**
+     * Relates test method name with the project to test below "projects" directory.
+     */
+    @Rule
+    public final TestName testName = new TestName();
+    
+    public final MavenRuntime mavenRuntime;
+
+    private File basedir;
+    
+    private MavenExecution mavenExecution;
+    
+    public DependencyCollectorTest( MavenRuntimeBuilder builder )
+        throws Exception
+    {
+        this.mavenRuntime = builder.build();
+    }
+    
+    @Before
+    public void setUp()
+        throws Exception
+    {
+        installMockDependencies();
+        
+        String testMethodName = removeMavenVersion( testName.getMethodName() );
+        
+        basedir = resources.getBasedir( testMethodName );
+
+        //@formatter:off
+        mavenExecution = mavenRuntime
+            .forProject( basedir )
+            .withCliOption( "-DmvnVersion=" + mavenRuntime.getMavenVersion() ) // Might be superfluous
+            .withCliOption( "-B" )
+            .withCliOption( "-V" );
+        //@formatter:on
+    }
+    
+    /**
+     * Test method cames in the form "testMethod>[version]", so remove the method name only.
+     * @param methodWithMavenVersion the JUnit test method.
+     * @return the method without the maven version.
+     */
+    private String removeMavenVersion( String methodWithMavenVersion )
+    {
+        int index = methodWithMavenVersion.indexOf( '[' );
+        return methodWithMavenVersion.substring( 0, index );
+    }
+
+    /**
+     * Install dependencies used for testing.
+     * 
+     * workaround to install the dependencies used in the tests, since
+     * mavenRuntime is not static and it is required to install them and 
+     * it needs to execute just once.
+     * TODO: improve this or find a solution to use mrm-maven-plugin with
+     * takari
+     * 
+     * @throws Exception if anything goes wrong.
+     */
+    private void installMockDependencies()
+        throws Exception
+    {
+        if ( testDependenciesInstalled )
+            return;
+
+        File basedir = resources.getBasedir( "mockDependencies" );
+        //@formatter:off
+        MavenExecutionResult result =
+            mavenRuntime
+                .forProject( basedir )
+                .withCliOption( "-DmvnVersion=" + mavenRuntime.getMavenVersion() ) // Might be superfluous
+                .withCliOption( "-B" )
+                .withCliOption( "-V" )
+                // We use verify to prevent running maven-install-plugin.
+                .execute( "clean", "install" );
+        //@formatter:on
+
+        result.assertErrorFreeLog();
+
+        testDependenciesInstalled = true;
+    }
+
+    /**
+     * collect dependencies, not informing the test dependencies from transitive dependencies.
+     * @throws Exception if anything goes wrong.
+     */
+    @Test
+    public void noTransitiveTestDep()
+        throws Exception
+    {
+        MavenExecutionResult result = mavenExecution.execute( "clean", "validate" );
+
+        result.assertErrorFreeLog();
+
+        // Check that the current plugins has been called at least once.
+        result.assertLogText( "[INFO] --- maven-dependency-collector-plugin:1.0.0:dependency-collector (id-dependency-collector) @ maven-dependency-collector-plugin-it ---" );
+
+        File logFile = new File(basedir, "log.txt");
+        String strLog = FileUtils.fileRead( logFile );
+        
+        String expected = 
+            "PROJECT-DEPENDENCY-COLLECTOR:maven-dependency-collector-plugin-it:jar:1.0.0-A:" + LS + 
+            "   org.apache.maven.plugin.dependency.collector.its:a:jar:1.0:compile" + LS +
+            "   org.apache.maven.plugin.dependency.collector.its:u:jar:1.0:test" + LS +
+            LS;
+
+        assertTrue( strLog.contains( expected ) );
+    }
+
+    /**
+     * collect dependencies, but don't inform in the tree those dependencies that are already shown in
+     * a a level closer to the root.
+     * @throws Exception if anything goes wrong.
+     */
+    @Test
+    public void noRepeatedDeps()
+        throws Exception
+    {
+        MavenExecutionResult result = mavenExecution.execute( "clean", "validate" );
+
+        result.assertErrorFreeLog();
+
+        // Check that the current plugins has been called at least once.
+        result.assertLogText( "[INFO] --- maven-dependency-collector-plugin:1.0.0:dependency-collector (id-dependency-collector) @ maven-dependency-collector-plugin-it ---" );
+
+        File logFile = new File(basedir, "log.txt");
+        String strLog = FileUtils.fileRead( logFile );
+        
+        String expected = 
+            "PROJECT-DEPENDENCY-COLLECTOR:maven-dependency-collector-plugin-it:jar:1.0.0-A:" + LS +
+            "   org.apache.maven.plugin.dependency.collector.its:c:jar:1.0:compile" + LS +
+            "      org.apache.maven.plugin.dependency.collector.its:b:jar:1.0:compile" + LS +
+            "   org.apache.maven.plugin.dependency.collector.its:b:jar:1.0:compile" + LS +
+            "      org.apache.maven.plugin.dependency.collector.its:a:jar:1.0:compile" + LS +
+            LS;
+
+        assertTrue( strLog.contains( expected ) );
+    }
+
+    /**
+     * collect test dependencies, and its respective compile scope dependencies are informed as "test" scope
+     * for the project. 
+     * @throws Exception if anything goes wrong.
+     */
+    @Test
+    public void transitiveTestDeps()
+        throws Exception
+    {
+        MavenExecutionResult result = mavenExecution.execute( "clean", "validate" );
+
+        result.assertErrorFreeLog();
+
+        // Check that the current plugins has been called at least once.
+        result.assertLogText( "[INFO] --- maven-dependency-collector-plugin:1.0.0:dependency-collector (id-dependency-collector) @ maven-dependency-collector-plugin-it ---" );
+
+        File logFile = new File(basedir, "log.txt");
+        String strLog = FileUtils.fileRead( logFile );
+        
+        String expected = 
+            "PROJECT-DEPENDENCY-COLLECTOR:maven-dependency-collector-plugin-it:jar:1.0.0-A:" + LS +
+            "   org.apache.maven.plugin.dependency.collector.its:t:jar:1.1:test" + LS +
+            "      org.apache.maven.plugin.dependency.collector.its:u:jar:1.0:test" + LS +
+            LS;
+
+        assertTrue( strLog.contains( expected ) );
+    }
+
+    /**
+     * collect dependencies, and inform when dependencyManagement supersedes 
+     * the declared (premanaged) version and scope.
+     * @throws Exception if anything goes wrong.
+     */
+    @Test
+    public void managedDepsAndScope()
+        throws Exception
+    {
+        MavenExecutionResult result = mavenExecution.execute( "clean", "validate" );
+
+        result.assertErrorFreeLog();
+
+        // Check that the current plugins has been called at least once.
+        result.assertLogText( "[INFO] --- maven-dependency-collector-plugin:1.0.0:dependency-collector (id-dependency-collector) @ maven-dependency-collector-plugin-it ---" );
+
+        File logFile = new File(basedir, "log.txt");
+        String strLog = FileUtils.fileRead( logFile );
+        
+        String expected = 
+            "PROJECT-DEPENDENCY-COLLECTOR:maven-dependency-collector-plugin-it:jar:1.0.0-A:" + LS +
+            "   org.apache.maven.plugin.dependency.collector.its:t:jar:1.1:compile" + LS +
+            "      org.apache.maven.plugin.dependency.collector.its:u:jar:1.1:test (version managed from 1.0; scope managed from compile)" + LS +
+            LS;
+
+        assertTrue( strLog.contains( expected ) );
+    }
+
+    /**
+     * collect dependencies, and inform when a dependency is optional.
+     * NOTE: Maven 3.0.x and 3.1+ behave differently. 
+     * @throws Exception if anything goes wrong.
+     */
+    @Test
+    public void optionalDep()
+        throws Exception
+    {
+        MavenExecutionResult result = mavenExecution.execute( "clean", "validate" );
+
+        result.assertErrorFreeLog();
+
+        // Check that the current plugins has been called at least once.
+        result.assertLogText( "[INFO] --- maven-dependency-collector-plugin:1.0.0:dependency-collector (id-dependency-collector) @ maven-dependency-collector-plugin-it ---" );
+
+        File logFile = new File(basedir, "log.txt");
+        String strLog = FileUtils.fileRead( logFile );
+
+        String expected; 
+        if ( isAtLeastMaven31() )
+        {
+            expected = 
+                "PROJECT-DEPENDENCY-COLLECTOR:maven-dependency-collector-plugin-it:jar:1.0.0-A:" + LS +
+                "   org.apache.maven.plugin.dependency.collector.its:c:jar:1.0:runtime (optional) " + LS + 
+                "      org.apache.maven.plugin.dependency.collector.its:b:jar:1.0:runtime (optional) " + LS + 
+                "         org.apache.maven.plugin.dependency.collector.its:a:jar:1.0:runtime (optional) " + LS + 
+                LS;
+        }
+        else
+        {
+            // Maven 3.0.x
+            expected = 
+                "PROJECT-DEPENDENCY-COLLECTOR:maven-dependency-collector-plugin-it:jar:1.0.0-A:" + LS +
+                "   org.apache.maven.plugin.dependency.collector.its:c:jar:1.0:runtime (optional) " + LS + 
+                "      org.apache.maven.plugin.dependency.collector.its:b:jar:1.0:runtime" + LS + 
+                "         org.apache.maven.plugin.dependency.collector.its:a:jar:1.0:runtime" + LS + 
+                LS;
+        }
+
+        assertTrue( strLog.contains( expected ) );
+    }
+
+    /**
+     * collect dependencies, and not inform the transitive optional dependencies. 
+     * @throws Exception if anything goes wrong.
+     */
+    @Test
+    public void noTransitiveOptionalDep()
+        throws Exception
+    {
+        MavenExecutionResult result = mavenExecution.execute( "clean", "validate" );
+
+        result.assertErrorFreeLog();
+
+        // Check that the current plugins has been called at least once.
+        result.assertLogText( "[INFO] --- maven-dependency-collector-plugin:1.0.0:dependency-collector (id-dependency-collector) @ maven-dependency-collector-plugin-it ---" );
+
+        File logFile = new File(basedir, "log.txt");
+        String strLog = FileUtils.fileRead( logFile );
+        
+        String expected = 
+            "PROJECT-DEPENDENCY-COLLECTOR:maven-dependency-collector-plugin-it:jar:1.0.0-A:" + LS +
+            "   org.apache.maven.plugin.dependency.collector.its:d:jar:1.0:compile" + LS + 
+            LS;
+
+        assertTrue( strLog.contains( expected ) );
+    }
+    
+    /**
+     * collect dependencies, and inform when a dependency version is selected from a range. 
+     * @throws Exception if anything goes wrong.
+     */
+    @Test
+    public void versionConstraintDep()
+        throws Exception
+    {
+        MavenExecutionResult result = mavenExecution.execute( "clean", "validate" );
+
+        result.assertErrorFreeLog();
+
+        // Check that the current plugins has been called at least once.
+        result.assertLogText( "[INFO] --- maven-dependency-collector-plugin:1.0.0:dependency-collector (id-dependency-collector) @ maven-dependency-collector-plugin-it ---" );
+
+        File logFile = new File(basedir, "log.txt");
+        String strLog = FileUtils.fileRead( logFile );
+        
+        String expected = 
+            "PROJECT-DEPENDENCY-COLLECTOR:maven-dependency-collector-plugin-it:jar:1.0.0-A:" + LS +
+            "   org.apache.maven.plugin.dependency.collector.its:t:jar:1.1:compile (version selected from constraint [1.1,))" + LS +
+            "      org.apache.maven.plugin.dependency.collector.its:u:jar:1.0:compile" + LS +            
+            LS;
+
+        assertTrue( strLog.contains( expected ) );
+    }
+
+    /**
+     * collect dependencies, and not inform when a dependency is excluded.
+     * @throws Exception if anything goes wrong.
+     */
+    @Test
+    public void noExcludedDep()
+        throws Exception
+    {
+        MavenExecutionResult result = mavenExecution.execute( "clean", "validate" );
+        
+        result.assertErrorFreeLog();
+
+        // Check that the current plugins has been called at least once.
+        result.assertLogText( "[INFO] --- maven-dependency-collector-plugin:1.0.0:dependency-collector (id-dependency-collector) @ maven-dependency-collector-plugin-it ---" );
+
+        File logFile = new File(basedir, "log.txt");
+        String strLog = FileUtils.fileRead( logFile );
+
+        String expected = 
+            "PROJECT-DEPENDENCY-COLLECTOR:maven-dependency-collector-plugin-it:jar:1.0.0-A:" + LS +
+             "   org.apache.maven.plugin.dependency.collector.its:c:jar:1.0:compile" + LS + 
+             "      org.apache.maven.plugin.dependency.collector.its:b:jar:1.0:compile" + LS + 
+             "   org.apache.maven.plugin.dependency.collector.its:b:jar:1.0:compile" + LS + 
+           LS;
+
+        assertTrue( strLog.contains( expected ) );
+    }
+
+    private boolean isAtLeastMaven31()
+        throws Exception
+    {
+        return "3.1".compareTo( mavenRuntime.getMavenVersion() ) <= 0;
+    }
+}
\ No newline at end of file
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/managedDepsAndScope/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/managedDepsAndScope/pom.xml
new file mode 100644
index 0000000..06e17b6
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/managedDepsAndScope/pom.xml
@@ -0,0 +1,68 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>PROJECT-DEPENDENCY-COLLECTOR</groupId>
+  <artifactId>maven-dependency-collector-plugin-it</artifactId>
+  <version>1.0.0-A</version>
+  
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+        <artifactId>u</artifactId>
+        <version>1.1</version>
+        <scope>test</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+  
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>t</artifactId>
+      <version>1.1</version>
+    </dependency>
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-collector-plugin</artifactId>
+        <version>${it-plugin.version}</version>
+        <configuration>
+          <localRepositoryPath>${localRepositoryPath}</localRepositoryPath>
+          <mvnVersion>${mvnVersion}</mvnVersion>
+        </configuration>
+        <executions>
+          <execution>
+            <id>id-dependency-collector</id>
+            <goals>
+              <goal>dependency-collector</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/a10/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/a10/pom.xml
new file mode 100644
index 0000000..d0d62cf
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/a10/pom.xml
@@ -0,0 +1,34 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+  <artifactId>a</artifactId>
+  <version>1.0</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>t</artifactId>
+      <version>1.0</version>
+      <scope>test</scope>    
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/b10/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/b10/pom.xml
new file mode 100644
index 0000000..5c026c5
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/b10/pom.xml
@@ -0,0 +1,33 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+  <artifactId>b</artifactId>
+  <version>1.0</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>a</artifactId>
+      <version>1.0</version>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/c10/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/c10/pom.xml
new file mode 100644
index 0000000..9049b83
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/c10/pom.xml
@@ -0,0 +1,33 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+  <artifactId>c</artifactId>
+  <version>1.0</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>b</artifactId>
+      <version>1.0</version>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/d10/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/d10/pom.xml
new file mode 100644
index 0000000..0b20d79
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/d10/pom.xml
@@ -0,0 +1,34 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+  <artifactId>d</artifactId>
+  <version>1.0</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>c</artifactId>
+      <version>1.0</version>
+      <optional>true</optional>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/pom.xml
new file mode 100644
index 0000000..f9f6abb
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/pom.xml
@@ -0,0 +1,37 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+  <artifactId>all-dependencies-project</artifactId>
+  <version>1.0</version>
+  <packaging>pom</packaging>
+  
+  <modules>
+    <module>u10</module>
+    <module>u11</module>
+    <module>t10</module>
+    <module>t11</module>
+    <module>a10</module>
+    <module>b10</module>
+    <module>c10</module>
+    <module>d10</module>
+  </modules>
+</project>
\ No newline at end of file
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/t10/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/t10/pom.xml
new file mode 100644
index 0000000..46221db
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/t10/pom.xml
@@ -0,0 +1,25 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+  <artifactId>t</artifactId>
+  <version>1.0</version>
+</project>
\ No newline at end of file
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/t11/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/t11/pom.xml
new file mode 100644
index 0000000..c7d8e8d
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/t11/pom.xml
@@ -0,0 +1,33 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+  <artifactId>t</artifactId>
+  <version>1.1</version>
+  
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>u</artifactId>
+      <version>1.0</version>    
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/u10/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/u10/pom.xml
new file mode 100644
index 0000000..d43b1da
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/u10/pom.xml
@@ -0,0 +1,25 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+  <artifactId>u</artifactId>
+  <version>1.0</version>
+</project>
\ No newline at end of file
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/u11/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/u11/pom.xml
new file mode 100644
index 0000000..8e08eee
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/mockDependencies/u11/pom.xml
@@ -0,0 +1,25 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+  <artifactId>u</artifactId>
+  <version>1.1</version>
+</project>
\ No newline at end of file
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/noExcludedDep/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/noExcludedDep/pom.xml
new file mode 100644
index 0000000..9d905d9
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/noExcludedDep/pom.xml
@@ -0,0 +1,68 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>PROJECT-DEPENDENCY-COLLECTOR</groupId>
+  <artifactId>maven-dependency-collector-plugin-it</artifactId>
+  <version>1.0.0-A</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>c</artifactId>
+      <version>1.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>b</artifactId>
+      <version>1.0</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+          <artifactId>a</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-collector-plugin</artifactId>
+        <version>${it-plugin.version}</version>
+        <configuration>
+          <localRepositoryPath>${localRepositoryPath}</localRepositoryPath>
+          <mvnVersion>${mvnVersion}</mvnVersion>
+        </configuration>
+        <executions>
+          <execution>
+            <id>id-dependency-collector</id>
+            <goals>
+              <goal>dependency-collector</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/noRepeatedDeps/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/noRepeatedDeps/pom.xml
new file mode 100644
index 0000000..33959c1
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/noRepeatedDeps/pom.xml
@@ -0,0 +1,62 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>PROJECT-DEPENDENCY-COLLECTOR</groupId>
+  <artifactId>maven-dependency-collector-plugin-it</artifactId>
+  <version>1.0.0-A</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>c</artifactId>
+      <version>1.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>b</artifactId>
+      <version>1.0</version>
+    </dependency>
+</dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-collector-plugin</artifactId>
+        <version>${it-plugin.version}</version>
+        <configuration>
+          <localRepositoryPath>${localRepositoryPath}</localRepositoryPath>
+          <mvnVersion>${mvnVersion}</mvnVersion>
+        </configuration>
+        <executions>
+          <execution>
+            <id>id-dependency-collector</id>
+            <goals>
+              <goal>dependency-collector</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/noTransitiveOptionalDep/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/noTransitiveOptionalDep/pom.xml
new file mode 100644
index 0000000..003b6a3
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/noTransitiveOptionalDep/pom.xml
@@ -0,0 +1,57 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>PROJECT-DEPENDENCY-COLLECTOR</groupId>
+  <artifactId>maven-dependency-collector-plugin-it</artifactId>
+  <version>1.0.0-A</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>d</artifactId>
+      <version>1.0</version>
+    </dependency>
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-collector-plugin</artifactId>
+        <version>${it-plugin.version}</version>
+        <configuration>
+          <localRepositoryPath>${localRepositoryPath}</localRepositoryPath>
+          <mvnVersion>${mvnVersion}</mvnVersion>
+        </configuration>
+        <executions>
+          <execution>
+            <id>id-dependency-collector</id>
+            <goals>
+              <goal>dependency-collector</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/noTransitiveTestDep/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/noTransitiveTestDep/pom.xml
new file mode 100644
index 0000000..47ae154
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/noTransitiveTestDep/pom.xml
@@ -0,0 +1,63 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>PROJECT-DEPENDENCY-COLLECTOR</groupId>
+  <artifactId>maven-dependency-collector-plugin-it</artifactId>
+  <version>1.0.0-A</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>a</artifactId>
+      <version>1.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>u</artifactId>
+      <version>1.0</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-collector-plugin</artifactId>
+        <version>${it-plugin.version}</version>
+        <configuration>
+          <localRepositoryPath>${localRepositoryPath}</localRepositoryPath>
+          <mvnVersion>${mvnVersion}</mvnVersion>
+        </configuration>
+        <executions>
+          <execution>
+            <id>id-dependency-collector</id>
+            <goals>
+              <goal>dependency-collector</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/optionalDep/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/optionalDep/pom.xml
new file mode 100644
index 0000000..12c80e7
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/optionalDep/pom.xml
@@ -0,0 +1,59 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>PROJECT-DEPENDENCY-COLLECTOR</groupId>
+  <artifactId>maven-dependency-collector-plugin-it</artifactId>
+  <version>1.0.0-A</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>c</artifactId>
+      <version>1.0</version>
+      <scope>runtime</scope>
+      <optional>true</optional>
+    </dependency>
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-collector-plugin</artifactId>
+        <version>${it-plugin.version}</version>
+        <configuration>
+          <localRepositoryPath>${localRepositoryPath}</localRepositoryPath>
+          <mvnVersion>${mvnVersion}</mvnVersion>
+        </configuration>
+        <executions>
+          <execution>
+            <id>id-dependency-collector</id>
+            <goals>
+              <goal>dependency-collector</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/transitiveTestDeps/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/transitiveTestDeps/pom.xml
new file mode 100644
index 0000000..38e7e4f
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/transitiveTestDeps/pom.xml
@@ -0,0 +1,58 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>PROJECT-DEPENDENCY-COLLECTOR</groupId>
+  <artifactId>maven-dependency-collector-plugin-it</artifactId>
+  <version>1.0.0-A</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>t</artifactId>
+      <version>1.1</version>
+      <scope>test</scope>
+    </dependency>
+</dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-collector-plugin</artifactId>
+        <version>${it-plugin.version}</version>
+        <configuration>
+          <localRepositoryPath>${localRepositoryPath}</localRepositoryPath>
+          <mvnVersion>${mvnVersion}</mvnVersion>
+        </configuration>
+        <executions>
+          <execution>
+            <id>id-dependency-collector</id>
+            <goals>
+              <goal>dependency-collector</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/maven-dependency-collector-plugin/src/test/projects/versionConstraintDep/pom.xml b/src/it/maven-dependency-collector-plugin/src/test/projects/versionConstraintDep/pom.xml
new file mode 100644
index 0000000..947f236
--- /dev/null
+++ b/src/it/maven-dependency-collector-plugin/src/test/projects/versionConstraintDep/pom.xml
@@ -0,0 +1,57 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>PROJECT-DEPENDENCY-COLLECTOR</groupId>
+  <artifactId>maven-dependency-collector-plugin-it</artifactId>
+  <version>1.0.0-A</version>
+  
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin.dependency.collector.its</groupId>
+      <artifactId>t</artifactId>
+      <version>[1.1,)</version>
+    </dependency>
+</dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-collector-plugin</artifactId>
+        <version>${it-plugin.version}</version>
+        <configuration>
+          <localRepositoryPath>${localRepositoryPath}</localRepositoryPath>
+          <mvnVersion>${mvnVersion}</mvnVersion>
+        </configuration>
+        <executions>
+          <execution>
+            <id>id-dependency-collector</id>
+            <goals>
+              <goal>dependency-collector</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/CollectorResult.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/CollectorResult.java
index f7b8809..8f393a5 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/CollectorResult.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/CollectorResult.java
@@ -22,6 +22,7 @@ package org.apache.maven.shared.transfer.dependencies.collect;
 import java.util.List;
 
 import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.shared.dependency.graph.DependencyNode;
 
 /**
  * 
@@ -34,4 +35,9 @@ public interface CollectorResult
      * @return {@link ArtifactRepository}
      */
     List<ArtifactRepository> getRemoteRepositories();
+
+    /***
+     * @return the root of the dependency graph
+     */
+    DependencyNode getDependencyGraphRoot();
 }
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/DependencyCollector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/DependencyCollector.java
index 2134776..700d66c 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/DependencyCollector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/DependencyCollector.java
@@ -70,4 +70,14 @@ public interface DependencyCollector
     CollectorResult collectDependencies( ProjectBuildingRequest buildingRequest, Model root )
                     throws DependencyCollectorException;
 
+    /**
+     * @param buildingRequest {@link ProjectBuildingRequest}.
+     * @return {@link CollectorResult}
+     * @throws DependencyCollectorException in case of an error which can be a component lookup error or an error while
+     *             trying to collect the dependencies.
+     * @throws IllegalArgumentException in case of parameter <code>buildingRequest</code> is <code>null</code>
+     */
+    CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+        throws DependencyCollectorException;
+
 }
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/DefaultDependencyCollector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/DefaultDependencyCollector.java
index 9904731..90548c3 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/DefaultDependencyCollector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/DefaultDependencyCollector.java
@@ -104,6 +104,21 @@ class DefaultDependencyCollector implements DependencyCollector, Contextualizabl
         }
     }
 
+    @Override
+    public CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+        throws DependencyCollectorException
+    {
+        validateBuildingRequest( buildingRequest );
+        try
+        {
+            return getMavenDependencyCollector( buildingRequest ).collectDependenciesGraph( buildingRequest );
+        }
+        catch ( ComponentLookupException e )
+        {
+            throw new DependencyCollectorException( e.getMessage(), e );
+        }
+    }
+
     private void validateParameters( ProjectBuildingRequest buildingRequest, DependableCoordinate root )
     {
         validateBuildingRequest( buildingRequest );
@@ -167,6 +182,7 @@ class DefaultDependencyCollector implements DependencyCollector, Contextualizabl
      * @param context Plexus context to inject.
      * @throws ContextException if the PlexusContainer could not be located.
      */
+    @Override
     public void contextualize( Context context )
         throws ContextException
     {
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30CollectorResult.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30CollectorResult.java
index f07c10f..bebfe96 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30CollectorResult.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30CollectorResult.java
@@ -20,16 +20,24 @@ package org.apache.maven.shared.transfer.dependencies.collect.internal;
  */
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.maven.RepositoryUtils;
 import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.shared.dependency.graph.internal.DefaultDependencyNode;
 import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult;
+import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollectorException;
+import org.sonatype.aether.artifact.Artifact;
 import org.sonatype.aether.collection.CollectResult;
+import org.sonatype.aether.graph.Dependency;
 import org.sonatype.aether.graph.DependencyNode;
 import org.sonatype.aether.graph.DependencyVisitor;
 import org.sonatype.aether.repository.RemoteRepository;
+import org.sonatype.aether.version.VersionConstraint;
 
 /**
  * CollectorResult wrapper around {@link CollectResult} 
@@ -82,4 +90,81 @@ class Maven30CollectorResult implements CollectorResult
         return mavenRepositories;
     }
 
+    @Override
+    public org.apache.maven.shared.dependency.graph.DependencyNode getDependencyGraphRoot()
+    {
+        DependencyNode root = collectResult.getRoot();
+        org.apache.maven.artifact.Artifact rootArtifact = getDependencyArtifact( root.getDependency() );
+
+        return buildDependencyNode( null, root, rootArtifact, null );
+    }
+
+    // CHECKSTYLE_OFF: LineLength
+    private org.apache.maven.shared.dependency.graph.DependencyNode buildDependencyNode( org.apache.maven.shared.dependency.graph.DependencyNode parent,
+                                                                                         DependencyNode node,
+                                                                                         org.apache.maven.artifact.Artifact artifact,
+                                                                                         ArtifactFilter filter )
+    // CHECKSTYLE_ON: LineLength
+    {
+        String premanagedVersion = node.getPremanagedVersion();
+        String premanagedScope = node.getPremanagedScope();
+
+        Boolean optional = null;
+        if ( node.getDependency() != null )
+        {
+            optional = node.getDependency().isOptional();
+        }
+
+        DefaultDependencyNode current =
+            new DefaultDependencyNode( parent, artifact, premanagedVersion, premanagedScope,
+                                       getVersionSelectedFromRange( node.getVersionConstraint() ), optional );
+
+        List<org.apache.maven.shared.dependency.graph.DependencyNode> nodes =
+            new ArrayList<org.apache.maven.shared.dependency.graph.DependencyNode>( node.getChildren().size() );
+        for ( DependencyNode child : node.getChildren() )
+        {
+            org.apache.maven.artifact.Artifact childArtifact = getDependencyArtifact( child.getDependency() );
+
+            if ( ( filter == null ) || filter.include( childArtifact ) )
+            {
+                nodes.add( buildDependencyNode( current, child, childArtifact, filter ) );
+            }
+        }
+
+        current.setChildren( Collections.unmodifiableList( nodes ) );
+
+        return current;
+    }
+
+    private String getVersionSelectedFromRange( VersionConstraint constraint )
+    {
+        if ( ( constraint == null ) || ( constraint.getVersion() != null ) || ( constraint.getRanges().isEmpty() ) )
+        {
+            return null;
+        }
+
+        return constraint.getRanges().iterator().next().toString();
+    }
+
+    private org.apache.maven.artifact.Artifact getDependencyArtifact( Dependency dep )
+    {
+        Artifact artifact = dep.getArtifact();
+
+        try
+        {
+            org.apache.maven.artifact.Artifact mavenArtifact =
+                (org.apache.maven.artifact.Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
+                                                                     Artifact.class, artifact );
+
+            mavenArtifact.setScope( dep.getScope() );
+            mavenArtifact.setOptional( dep.isOptional() );
+
+            return mavenArtifact;
+        }
+        catch ( DependencyCollectorException e )
+        {
+            // ReflectionException should not happen
+            throw new RuntimeException( e.getMessage(), e );
+        }
+    }
 }
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConfigUtils.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConfigUtils.java
new file mode 100644
index 0000000..bc7172e
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConfigUtils.java
@@ -0,0 +1,342 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.sonatype.aether.RepositorySystemSession;
+
+/**
+ * This class is a copy of their homonymous in the Eclipse Aether library, adapted to work with Sonatype Aether.
+ * 
+ * @author Gabriel Belingueres
+ */
+public class Maven30ConfigUtils
+{
+
+    private Maven30ConfigUtils()
+    {
+        // hide constructor
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param properties The configuration properties to read, must not be {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set, may be {@code null}.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value or {@code null} if none.
+     */
+    public static Object getObject( Map<?, ?> properties, Object defaultValue, String... keys )
+    {
+        for ( String key : keys )
+        {
+            Object value = properties.get( key );
+
+            if ( value != null )
+            {
+                return value;
+            }
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param session The repository system session from which to read the configuration property, must not be
+     *            {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set, may be {@code null}.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value or {@code null} if none.
+     */
+    public static Object getObject( RepositorySystemSession session, Object defaultValue, String... keys )
+    {
+        return getObject( session.getConfigProperties(), defaultValue, keys );
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param properties The configuration properties to read, must not be {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set, may be {@code null}.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value or {@code null} if none.
+     */
+    public static String getString( Map<?, ?> properties, String defaultValue, String... keys )
+    {
+        for ( String key : keys )
+        {
+            Object value = properties.get( key );
+
+            if ( value instanceof String )
+            {
+                return (String) value;
+            }
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param session The repository system session from which to read the configuration property, must not be
+     *            {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set, may be {@code null}.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value or {@code null} if none.
+     */
+    public static String getString( RepositorySystemSession session, String defaultValue, String... keys )
+    {
+        return getString( session.getConfigProperties(), defaultValue, keys );
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param properties The configuration properties to read, must not be {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value.
+     */
+    public static int getInteger( Map<?, ?> properties, int defaultValue, String... keys )
+    {
+        for ( String key : keys )
+        {
+            Object value = properties.get( key );
+
+            if ( value instanceof Number )
+            {
+                return ( (Number) value ).intValue();
+            }
+
+            try
+            {
+                return Integer.valueOf( (String) value );
+            }
+            catch ( Exception e )
+            {
+                // try next key
+            }
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param session The repository system session from which to read the configuration property, must not be
+     *            {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value.
+     */
+    public static int getInteger( RepositorySystemSession session, int defaultValue, String... keys )
+    {
+        return getInteger( session.getConfigProperties(), defaultValue, keys );
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param properties The configuration properties to read, must not be {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value.
+     */
+    public static long getLong( Map<?, ?> properties, long defaultValue, String... keys )
+    {
+        for ( String key : keys )
+        {
+            Object value = properties.get( key );
+
+            if ( value instanceof Number )
+            {
+                return ( (Number) value ).longValue();
+            }
+
+            try
+            {
+                return Long.valueOf( (String) value );
+            }
+            catch ( Exception e )
+            {
+                // try next key
+            }
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param session The repository system session from which to read the configuration property, must not be
+     *            {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value.
+     */
+    public static long getLong( RepositorySystemSession session, long defaultValue, String... keys )
+    {
+        return getLong( session.getConfigProperties(), defaultValue, keys );
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param properties The configuration properties to read, must not be {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value.
+     */
+    public static boolean getBoolean( Map<?, ?> properties, boolean defaultValue, String... keys )
+    {
+        for ( String key : keys )
+        {
+            Object value = properties.get( key );
+
+            if ( value instanceof Boolean )
+            {
+                return ( (Boolean) value ).booleanValue();
+            }
+            else if ( value instanceof String )
+            {
+                return Boolean.parseBoolean( (String) value );
+            }
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param session The repository system session from which to read the configuration property, must not be
+     *            {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value.
+     */
+    public static boolean getBoolean( RepositorySystemSession session, boolean defaultValue, String... keys )
+    {
+        return getBoolean( session.getConfigProperties(), defaultValue, keys );
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param properties The configuration properties to read, must not be {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set, may be {@code null}.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value or {@code null} if none.
+     */
+    public static List<?> getList( Map<?, ?> properties, List<?> defaultValue, String... keys )
+    {
+        for ( String key : keys )
+        {
+            Object value = properties.get( key );
+
+            if ( value instanceof List )
+            {
+                return (List<?>) value;
+            }
+            else if ( value instanceof Collection )
+            {
+                return Collections.unmodifiableList( new ArrayList<Object>( (Collection<?>) value ) );
+            }
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param session The repository system session from which to read the configuration property, must not be
+     *            {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set, may be {@code null}.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value or {@code null} if none.
+     */
+    public static List<?> getList( RepositorySystemSession session, List<?> defaultValue, String... keys )
+    {
+        return getList( session.getConfigProperties(), defaultValue, keys );
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param properties The configuration properties to read, must not be {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set, may be {@code null}.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value or {@code null} if none.
+     */
+    public static Map<?, ?> getMap( Map<?, ?> properties, Map<?, ?> defaultValue, String... keys )
+    {
+        for ( String key : keys )
+        {
+            Object value = properties.get( key );
+
+            if ( value instanceof Map )
+            {
+                return (Map<?, ?>) value;
+            }
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Gets the specified configuration property.
+     * 
+     * @param session The repository system session from which to read the configuration property, must not be
+     *            {@code null}.
+     * @param defaultValue The default value to return in case the property isn't set, may be {@code null}.
+     * @param keys The properties to read, must not be {@code null}. The specified keys are read one after one until a
+     *            valid value is found.
+     * @return The property value or {@code null} if none.
+     */
+    public static Map<?, ?> getMap( RepositorySystemSession session, Map<?, ?> defaultValue, String... keys )
+    {
+        return getMap( session.getConfigProperties(), defaultValue, keys );
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictIdSorter.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictIdSorter.java
new file mode 100644
index 0000000..f8ea6de
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictIdSorter.java
@@ -0,0 +1,369 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.sonatype.aether.RepositoryException;
+import org.sonatype.aether.collection.DependencyGraphTransformationContext;
+import org.sonatype.aether.collection.DependencyGraphTransformer;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.util.graph.transformer.ConflictMarker;
+import org.sonatype.aether.util.graph.transformer.TransformationContextKeys;
+
+/**
+ * This class is a copy of their homonymous in the Eclipse Aether library, adapted to work with Sonatype Aether.
+ * 
+ * @author Gabriel Belingueres
+ */
+public class Maven30ConflictIdSorter
+    implements DependencyGraphTransformer
+{
+
+    public DependencyNode transformGraph( DependencyNode node, DependencyGraphTransformationContext context )
+        throws RepositoryException
+    {
+        Map<?, ?> conflictIds = (Map<?, ?>) context.get( TransformationContextKeys.CONFLICT_IDS );
+        if ( conflictIds == null )
+        {
+            ConflictMarker marker = new ConflictMarker();
+            marker.transformGraph( node, context );
+
+            conflictIds = (Map<?, ?>) context.get( TransformationContextKeys.CONFLICT_IDS );
+        }
+
+//    @SuppressWarnings( "unchecked" )
+//    Map<String, Object> stats = (Map<String, Object>) context.get( TransformationContextKeys.STATS );
+//    long time1 = System.currentTimeMillis();
+
+        Map<Object, ConflictId> ids = new LinkedHashMap<Object, ConflictId>( 256 );
+
+        // CHECKSTYLE_OFF: AvoidNestedBlocks
+        {
+            ConflictId id = null;
+            Object key = conflictIds.get( node );
+            if ( key != null )
+            {
+                id = new ConflictId( key, 0 );
+                ids.put( key, id );
+            }
+
+            Map<DependencyNode, Object> visited = new IdentityHashMap<DependencyNode, Object>( conflictIds.size() );
+
+            buildConflitIdDAG( ids, node, id, 0, visited, conflictIds );
+        }
+        // CHECKSTYLE_NO: AvoidNestedBlocks
+
+//    long time2 = System.currentTimeMillis();
+
+        topsortConflictIds( ids.values(), context );
+//    int cycles = topsortConflictIds( ids.values(), context );
+
+//    if ( stats != null )
+//    {
+//        long time3 = System.currentTimeMillis();
+//        stats.put( "ConflictIdSorter.graphTime", time2 - time1 );
+//        stats.put( "ConflictIdSorter.topsortTime", time3 - time2 );
+//        stats.put( "ConflictIdSorter.conflictIdCount", ids.size() );
+//        stats.put( "ConflictIdSorter.conflictIdCycleCount", cycles );
+//    }
+
+        return node;
+    }
+
+    private void buildConflitIdDAG( Map<Object, ConflictId> ids, DependencyNode node, ConflictId id, int depth,
+                                    Map<DependencyNode, Object> visited, Map<?, ?> conflictIds )
+    {
+        if ( visited.put( node, Boolean.TRUE ) != null )
+        {
+            return;
+        }
+
+        depth++;
+
+        for ( DependencyNode child : node.getChildren() )
+        {
+            Object key = conflictIds.get( child );
+            ConflictId childId = ids.get( key );
+            if ( childId == null )
+            {
+                childId = new ConflictId( key, depth );
+                ids.put( key, childId );
+            }
+            else
+            {
+                childId.pullup( depth );
+            }
+
+            if ( id != null )
+            {
+                id.add( childId );
+            }
+
+            buildConflitIdDAG( ids, child, childId, depth, visited, conflictIds );
+        }
+    }
+
+    private int topsortConflictIds( Collection<ConflictId> conflictIds, DependencyGraphTransformationContext context )
+    {
+        List<Object> sorted = new ArrayList<Object>( conflictIds.size() );
+
+        RootQueue roots = new RootQueue( conflictIds.size() / 2 );
+        for ( ConflictId id : conflictIds )
+        {
+            if ( id.inDegree <= 0 )
+            {
+                roots.add( id );
+            }
+        }
+
+        processRoots( sorted, roots );
+
+        boolean cycle = sorted.size() < conflictIds.size();
+
+        while ( sorted.size() < conflictIds.size() )
+        {
+            // cycle -> deal gracefully with nodes still having positive in-degree
+
+            ConflictId nearest = null;
+            for ( ConflictId id : conflictIds )
+            {
+                if ( id.inDegree <= 0 )
+                {
+                    continue;
+                }
+                if ( nearest == null || id.minDepth < nearest.minDepth
+                    || ( id.minDepth == nearest.minDepth && id.inDegree < nearest.inDegree ) )
+                {
+                    nearest = id;
+                }
+            }
+
+            nearest.inDegree = 0;
+            roots.add( nearest );
+
+            processRoots( sorted, roots );
+        }
+
+        Collection<Collection<Object>> cycles = Collections.emptySet();
+        if ( cycle )
+        {
+            cycles = findCycles( conflictIds );
+        }
+
+        context.put( TransformationContextKeys.SORTED_CONFLICT_IDS, sorted );
+        // context.put( TransformationContextKeys.CYCLIC_CONFLICT_IDS, sorted );
+        context.put( Maven30ConflictResolver.CYCLIC_CONFLICT_IDS, cycles );
+
+        return cycles.size();
+    }
+
+    private void processRoots( List<Object> sorted, RootQueue roots )
+    {
+        while ( !roots.isEmpty() )
+        {
+            ConflictId root = roots.remove();
+
+            sorted.add( root.key );
+
+            for ( ConflictId child : root.children )
+            {
+                child.inDegree--;
+                if ( child.inDegree == 0 )
+                {
+                    roots.add( child );
+                }
+            }
+        }
+    }
+
+    private Collection<Collection<Object>> findCycles( Collection<ConflictId> conflictIds )
+    {
+        Collection<Collection<Object>> cycles = new HashSet<Collection<Object>>();
+
+        Map<Object, Integer> stack = new HashMap<Object, Integer>( 128 );
+        Map<ConflictId, Object> visited = new IdentityHashMap<ConflictId, Object>( conflictIds.size() );
+        for ( ConflictId id : conflictIds )
+        {
+            findCycles( id, visited, stack, cycles );
+        }
+
+        return cycles;
+    }
+
+    private void findCycles( ConflictId id, Map<ConflictId, Object> visited, Map<Object, Integer> stack,
+                             Collection<Collection<Object>> cycles )
+    {
+        Integer depth = stack.put( id.key, stack.size() );
+        if ( depth != null )
+        {
+            stack.put( id.key, depth );
+            Collection<Object> cycle = new HashSet<Object>();
+            for ( Map.Entry<Object, Integer> entry : stack.entrySet() )
+            {
+                if ( entry.getValue() >= depth )
+                {
+                    cycle.add( entry.getKey() );
+                }
+            }
+            cycles.add( cycle );
+        }
+        else
+        {
+            if ( visited.put( id, Boolean.TRUE ) == null )
+            {
+                for ( ConflictId childId : id.children )
+                {
+                    findCycles( childId, visited, stack, cycles );
+                }
+            }
+            stack.remove( id.key );
+        }
+    }
+
+    static final class ConflictId
+    {
+
+        final Object key;
+
+        Collection<ConflictId> children = Collections.emptySet();
+
+        int inDegree;
+
+        int minDepth;
+
+        ConflictId( Object key, int depth )
+        {
+            this.key = key;
+            this.minDepth = depth;
+        }
+
+        public void add( ConflictId child )
+        {
+            if ( children.isEmpty() )
+            {
+                children = new HashSet<ConflictId>();
+            }
+            if ( children.add( child ) )
+            {
+                child.inDegree++;
+            }
+        }
+
+        public void pullup( int depth )
+        {
+            if ( depth < minDepth )
+            {
+                minDepth = depth;
+                depth++;
+                for ( ConflictId child : children )
+                {
+                    child.pullup( depth );
+                }
+            }
+        }
+
+        @Override
+        public boolean equals( Object obj )
+        {
+            if ( this == obj )
+            {
+                return true;
+            }
+            else if ( !( obj instanceof ConflictId ) )
+            {
+                return false;
+            }
+            ConflictId that = (ConflictId) obj;
+            return this.key.equals( that.key );
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return key.hashCode();
+        }
+
+        @Override
+        public String toString()
+        {
+            return key + " @ " + minDepth + " <" + inDegree;
+        }
+
+    }
+
+    static final class RootQueue
+    {
+
+        private int nextOut;
+
+        private int nextIn;
+
+        private ConflictId[] ids;
+
+        RootQueue( int capacity )
+        {
+            ids = new ConflictId[capacity + 16];
+        }
+
+        boolean isEmpty()
+        {
+            return nextOut >= nextIn;
+        }
+
+        void add( ConflictId id )
+        {
+            if ( nextOut >= nextIn && nextOut > 0 )
+            {
+                nextIn -= nextOut;
+                nextOut = 0;
+            }
+            if ( nextIn >= ids.length )
+            {
+                ConflictId[] tmp = new ConflictId[ids.length + ids.length / 2 + 16];
+                System.arraycopy( ids, nextOut, tmp, 0, nextIn - nextOut );
+                ids = tmp;
+                nextIn -= nextOut;
+                nextOut = 0;
+            }
+            int i;
+            for ( i = nextIn - 1; i >= nextOut && id.minDepth < ids[i].minDepth; i-- )
+            {
+                ids[i + 1] = ids[i];
+            }
+            ids[i + 1] = id;
+            nextIn++;
+        }
+
+        ConflictId remove()
+        {
+            return ids[nextOut++];
+        }
+
+    }
+}
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictResolver.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictResolver.java
new file mode 100644
index 0000000..4ac601a
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictResolver.java
@@ -0,0 +1,1345 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.eclipse.aether.util.graph.transformer.ConflictResolver;
+import org.sonatype.aether.RepositoryException;
+import org.sonatype.aether.artifact.Artifact;
+import org.sonatype.aether.collection.DependencyGraphTransformationContext;
+import org.sonatype.aether.collection.DependencyGraphTransformer;
+import org.sonatype.aether.graph.Dependency;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.util.graph.DefaultDependencyNode;
+import org.sonatype.aether.util.graph.transformer.ConflictIdSorter;
+import org.sonatype.aether.util.graph.transformer.TransformationContextKeys;
+
+/**
+ * This class is a copy of their homonymous in the Eclipse Aether library, adapted to work with Sonatype Aether.
+ * 
+ * @author Gabriel Belingueres
+ */
+public class Maven30ConflictResolver
+    implements DependencyGraphTransformer
+{
+
+    /**
+     * NOTE: ADDED THIS CONSTANT THERE BECAUSE IT DOES NOT EXISTS IN SONATYPE AETHER 1.7 The key in the graph
+     * transformation context where a {@code Boolean} is stored that indicates whether the dependencies between conflict
+     * ids form a cycle.
+     * 
+     * @see ConflictIdSorter
+     */
+    public static final Object CYCLIC_CONFLICT_IDS = "cyclicConflictIds";
+
+    /**
+     * The key in the repository session's {@link RepositorySystemSession#getConfigProperties() configuration
+     * properties} used to store a {@link Boolean} flag controlling the transformer's verbose mode.
+     */
+    public static final String CONFIG_PROP_VERBOSE = "aether.conflictResolver.verbose";
+
+    /**
+     * The key in the dependency node's {@link DependencyNode#getData() custom data} under which a reference to the
+     * {@link DependencyNode} which has won the conflict is stored.
+     */
+    public static final String NODE_DATA_WINNER = "conflict.winner";
+
+    /**
+     * The key in the dependency node's {@link DependencyNode#getData() custom data} under which the scope of the
+     * dependency before scope derivation and conflict resolution is stored.
+     */
+    public static final String NODE_DATA_ORIGINAL_SCOPE = "conflict.originalScope";
+
+    /**
+     * The key in the dependency node's {@link DependencyNode#getData() custom data} under which the optional flag of
+     * the dependency before derivation and conflict resolution is stored.
+     */
+    public static final String NODE_DATA_ORIGINAL_OPTIONALITY = "conflict.originalOptionality";
+
+    private final VersionSelector versionSelector;
+
+    private final ScopeSelector scopeSelector;
+
+    private final ScopeDeriver scopeDeriver;
+
+    private final OptionalitySelector optionalitySelector;
+
+    private Maven30NodeData nodeData;
+
+    /**
+     * Creates a new conflict resolver instance with the specified hooks.
+     * 
+     * @param versionSelector The version selector to use, must not be {@code null}.
+     * @param scopeSelector The scope selector to use, must not be {@code null}.
+     * @param optionalitySelector The optionality selector ot use, must not be {@code null}.
+     * @param scopeDeriver The scope deriver to use, must not be {@code null}.
+     * @param nodeData the object where to save node data since Sonatype Aether 1.7 does not have that info inside the
+     *            DependencyNode.
+     */
+    public Maven30ConflictResolver( VersionSelector versionSelector, ScopeSelector scopeSelector,
+                                    OptionalitySelector optionalitySelector, ScopeDeriver scopeDeriver,
+                                    Maven30NodeData nodeData )
+    {
+        if ( versionSelector == null )
+        {
+            throw new IllegalArgumentException( "version selector not specified" );
+        }
+        this.versionSelector = versionSelector;
+        if ( scopeSelector == null )
+        {
+            throw new IllegalArgumentException( "scope selector not specified" );
+        }
+        this.scopeSelector = scopeSelector;
+        if ( scopeDeriver == null )
+        {
+            throw new IllegalArgumentException( "scope deriver not specified" );
+        }
+        this.scopeDeriver = scopeDeriver;
+        if ( optionalitySelector == null )
+        {
+            throw new IllegalArgumentException( "optionality selector not specified" );
+        }
+        this.optionalitySelector = optionalitySelector;
+        this.nodeData = nodeData;
+    }
+
+    public DependencyNode transformGraph( DependencyNode node, DependencyGraphTransformationContext context )
+        throws RepositoryException
+    {
+        List<?> sortedConflictIds = (List<?>) context.get( TransformationContextKeys.SORTED_CONFLICT_IDS );
+        if ( sortedConflictIds == null )
+        {
+            Maven30ConflictIdSorter sorter = new Maven30ConflictIdSorter();
+            sorter.transformGraph( node, context );
+
+            sortedConflictIds = (List<?>) context.get( TransformationContextKeys.SORTED_CONFLICT_IDS );
+        }
+
+//    @SuppressWarnings( "unchecked" )
+//    Map<String, Object> stats = (Map<String, Object>) context.get( TransformationContextKeys.STATS );
+//    long time1 = System.currentTimeMillis();
+
+        @SuppressWarnings( "unchecked" )
+        Collection<Collection<?>> conflictIdCycles =
+            (Collection<Collection<?>>) context.get( CYCLIC_CONFLICT_IDS );
+        if ( conflictIdCycles == null )
+        {
+            throw new RepositoryException( "conflict id cycles have not been identified" );
+        }
+
+        Map<?, ?> conflictIds = (Map<?, ?>) context.get( TransformationContextKeys.CONFLICT_IDS );
+        if ( conflictIds == null )
+        {
+            throw new RepositoryException( "conflict groups have not been identified" );
+        }
+
+        Map<Object, Collection<Object>> cyclicPredecessors = new HashMap<Object, Collection<Object>>();
+        for ( Collection<?> cycle : conflictIdCycles )
+        {
+            for ( Object conflictId : cycle )
+            {
+                Collection<Object> predecessors = cyclicPredecessors.get( conflictId );
+                if ( predecessors == null )
+                {
+                    predecessors = new HashSet<Object>();
+                    cyclicPredecessors.put( conflictId, predecessors );
+                }
+                predecessors.addAll( cycle );
+            }
+        }
+
+        State state = new State( node, conflictIds, sortedConflictIds.size(), context );
+        for ( Iterator<?> it = sortedConflictIds.iterator(); it.hasNext(); )
+        {
+            Object conflictId = it.next();
+
+            // reset data structures for next graph walk
+            state.prepare( conflictId, cyclicPredecessors.get( conflictId ) );
+
+            // find nodes with the current conflict id and while walking the graph (more deeply), nuke leftover losers
+            gatherConflictItems( node, state );
+
+            // now that we know the min depth of the parents, update depth of conflict items
+            state.finish();
+
+            // earlier runs might have nuked all parents of the current conflict id, so it might not exist anymore
+            if ( !state.items.isEmpty() )
+            {
+                ConflictContext ctx = state.conflictCtx;
+                state.versionSelector.selectVersion( ctx );
+                if ( ctx.winner == null )
+                {
+                    throw new RepositoryException( "conflict resolver did not select winner among " + state.items );
+                }
+                DependencyNode winner = ctx.winner.node;
+
+                state.scopeSelector.selectScope( ctx );
+                if ( state.verbose )
+                {
+                    nodeData.putData( winner, NODE_DATA_ORIGINAL_SCOPE, winner.getDependency().getScope() );
+//                    winner.setData( NODE_DATA_ORIGINAL_SCOPE, winner.getDependency().getScope() );
+                }
+                winner.setScope( ctx.scope );
+
+                state.optionalitySelector.selectOptionality( ctx );
+                if ( state.verbose )
+                {
+                    nodeData.putData( winner, NODE_DATA_ORIGINAL_OPTIONALITY, winner.getDependency().isOptional() );
+//                    winner.setData( NODE_DATA_ORIGINAL_OPTIONALITY, winner.getDependency().isOptional() );
+                }
+                winner.getDependency().setOptional( ctx.optional );
+//            winner.setOptional( ctx.optional );
+
+                removeLosers( state );
+            }
+
+            // record the winner so we can detect leftover losers during future graph walks
+            state.winner();
+
+            // in case of cycles, trigger final graph walk to ensure all leftover losers are gone
+            if ( !it.hasNext() && !conflictIdCycles.isEmpty() && state.conflictCtx.winner != null )
+            {
+                DependencyNode winner = state.conflictCtx.winner.node;
+                state.prepare( state, null );
+                gatherConflictItems( winner, state );
+            }
+        }
+
+//    if ( stats != null )
+//    {
+//        long time2 = System.currentTimeMillis();
+//        stats.put( "ConflictResolver.totalTime", time2 - time1 );
+//        stats.put( "ConflictResolver.conflictItemCount", state.totalConflictItems );
+//    }
+
+        return node;
+    }
+
+    private boolean gatherConflictItems( DependencyNode node, State state )
+        throws RepositoryException
+{
+        Object conflictId = state.conflictIds.get( node );
+        if ( state.currentId.equals( conflictId ) )
+        {
+            // found it, add conflict item (if not already done earlier by another path)
+            state.add( node );
+            // we don't recurse here so we might miss losers beneath us, those will be nuked during future walks below
+        }
+        else if ( state.loser( node, conflictId ) )
+        {
+            // found a leftover loser (likely in a cycle) of an already processed conflict id, tell caller to nuke it
+            return false;
+        }
+        else if ( state.push( node, conflictId ) )
+        {
+            // found potential parent, no cycle and not visisted before with the same derived scope, so recurse
+            for ( Iterator<DependencyNode> it = node.getChildren().iterator(); it.hasNext(); )
+            {
+                DependencyNode child = it.next();
+                if ( !gatherConflictItems( child, state ) )
+                {
+                    it.remove();
+                }
+            }
+            state.pop();
+        }
+        return true;
+    }
+
+    private void removeLosers( State state )
+    {
+        ConflictItem winner = state.conflictCtx.winner;
+        List<DependencyNode> previousParent = null;
+        ListIterator<DependencyNode> childIt = null;
+        boolean conflictVisualized = false;
+        for ( ConflictItem item : state.items )
+        {
+            if ( item == winner )
+            {
+                continue;
+            }
+            if ( item.parent != previousParent )
+            {
+                childIt = item.parent.listIterator();
+                previousParent = item.parent;
+                conflictVisualized = false;
+            }
+            while ( childIt.hasNext() )
+            {
+                DependencyNode child = childIt.next();
+                if ( child == item.node )
+                {
+                    if ( state.verbose && !conflictVisualized && item.parent != winner.parent )
+                    {
+                        conflictVisualized = true;
+                        DependencyNode loser = new DefaultDependencyNode( child );
+                        nodeData.putData( loser, NODE_DATA_WINNER, winner.node );
+                        nodeData.putData( loser, NODE_DATA_ORIGINAL_SCOPE, loser.getDependency().getScope() );
+                        nodeData.putData( loser, NODE_DATA_ORIGINAL_OPTIONALITY, loser.getDependency().isOptional() );
+//                        loser.setData( NODE_DATA_WINNER, winner.node );
+//                        loser.setData( NODE_DATA_ORIGINAL_SCOPE, loser.getDependency().getScope() );
+//                        loser.setData( NODE_DATA_ORIGINAL_OPTIONALITY, loser.getDependency().isOptional() );
+                        loser.setScope( item.getScopes().iterator().next() );
+//                    loser.setChildren( Collections.<DependencyNode>emptyList() );
+                        childIt.set( loser );
+                    }
+                    else
+                    {
+                        childIt.remove();
+                    }
+                    break;
+                }
+            }
+        }
+        // there might still be losers beneath the winner (e.g. in case of cycles)
+        // those will be nuked during future graph walks when we include the winner in the recursion
+    }
+
+    final class NodeInfo
+    {
+
+        /**
+         * The smallest depth at which the node was seen, used for "the" depth of its conflict items.
+         */
+        int minDepth;
+
+        /**
+         * The set of derived scopes the node was visited with, used to check whether an already seen node needs to be
+         * revisited again in context of another scope. To conserve memory, we start with {@code String} and update to
+         * {@code Set<String>} if needed.
+         */
+        Object derivedScopes;
+
+        /**
+         * The set of derived optionalities the node was visited with, used to check whether an already seen node needs
+         * to be revisited again in context of another optionality. To conserve memory, encoded as bit field (bit 0 ->
+         * optional=false, bit 1 -> optional=true).
+         */
+        int derivedOptionalities;
+
+        /**
+         * The conflict items which are immediate children of the node, used to easily update those conflict items after
+         * a new parent scope/optionality was encountered.
+         */
+        List<ConflictItem> children;
+
+        static final int CHANGE_SCOPE = 0x01;
+
+        static final int CHANGE_OPTIONAL = 0x02;
+
+        private static final int OPT_FALSE = 0x01;
+
+        private static final int OPT_TRUE = 0x02;
+
+        NodeInfo( int depth, String derivedScope, boolean optional )
+        {
+            minDepth = depth;
+            derivedScopes = derivedScope;
+            derivedOptionalities = optional ? OPT_TRUE : OPT_FALSE;
+        }
+
+        @SuppressWarnings( "unchecked" )
+        int update( int depth, String derivedScope, boolean optional )
+        {
+            if ( depth < minDepth )
+            {
+                minDepth = depth;
+            }
+            int changes;
+            if ( derivedScopes.equals( derivedScope ) )
+            {
+                changes = 0;
+            }
+            else if ( derivedScopes instanceof Collection )
+            {
+                changes = ( (Collection<String>) derivedScopes ).add( derivedScope ) ? CHANGE_SCOPE : 0;
+            }
+            else
+            {
+                Collection<String> scopes = new HashSet<String>();
+                scopes.add( (String) derivedScopes );
+                scopes.add( derivedScope );
+                derivedScopes = scopes;
+                changes = CHANGE_SCOPE;
+            }
+            int bit = optional ? OPT_TRUE : OPT_FALSE;
+            if ( ( derivedOptionalities & bit ) == 0 )
+            {
+                derivedOptionalities |= bit;
+                changes |= CHANGE_OPTIONAL;
+            }
+            return changes;
+        }
+
+        void add( ConflictItem item )
+        {
+            if ( children == null )
+            {
+                children = new ArrayList<ConflictItem>( 1 );
+            }
+            children.add( item );
+        }
+
+    }
+
+    final class State
+    {
+
+        /**
+         * The conflict id currently processed.
+         */
+        Object currentId;
+
+        /**
+         * Stats counter.
+         */
+        int totalConflictItems;
+
+        /**
+         * Flag whether we should keep losers in the graph to enable visualization/troubleshooting of conflicts.
+         */
+        final boolean verbose;
+
+        /**
+         * A mapping from conflict id to winner node, helps to recognize nodes that have their effective
+         * scope&optionality set or are leftovers from previous removals.
+         */
+        final Map<Object, DependencyNode> resolvedIds;
+
+        /**
+         * The set of conflict ids which could apply to ancestors of nodes with the current conflict id, used to avoid
+         * recursion early on. This is basically a superset of the key set of resolvedIds, the additional ids account
+         * for cyclic dependencies.
+         */
+        final Collection<Object> potentialAncestorIds;
+
+        /**
+         * The output from the conflict marker
+         */
+        final Map<?, ?> conflictIds;
+
+        /**
+         * The conflict items we have gathered so far for the current conflict id.
+         */
+        final List<ConflictItem> items;
+
+        /**
+         * The (conceptual) mapping from nodes to extra infos, technically keyed by the node's child list which better
+         * captures the identity of a node since we're basically concerned with effects towards children.
+         */
+        final Map<List<DependencyNode>, NodeInfo> infos;
+
+        /**
+         * The set of nodes on the DFS stack to detect cycles, technically keyed by the node's child list to match the
+         * dirty graph structure produced by the dependency collector for cycles.
+         */
+        final Map<List<DependencyNode>, Object> stack;
+
+        /**
+         * The stack of parent nodes.
+         */
+        final List<DependencyNode> parentNodes;
+
+        /**
+         * The stack of derived scopes for parent nodes.
+         */
+        final List<String> parentScopes;
+
+        /**
+         * The stack of derived optional flags for parent nodes.
+         */
+        final List<Boolean> parentOptionals;
+
+        /**
+         * The stack of node infos for parent nodes, may contain {@code null} which is used to disable creating new
+         * conflict items when visiting their parent again (conflict items are meant to be unique by parent-node combo).
+         */
+        final List<NodeInfo> parentInfos;
+
+        /**
+         * The conflict context passed to the version/scope/optionality selectors, updated as we move along rather than
+         * recreated to avoid tmp objects.
+         */
+        final ConflictContext conflictCtx;
+
+        /**
+         * The scope context passed to the scope deriver, updated as we move along rather than recreated to avoid tmp
+         * objects.
+         */
+        final ScopeContext scopeCtx;
+
+        /**
+         * The effective version selector, i.e. after initialization.
+         */
+        final VersionSelector versionSelector;
+
+        /**
+         * The effective scope selector, i.e. after initialization.
+         */
+        final ScopeSelector scopeSelector;
+
+        /**
+         * The effective scope deriver, i.e. after initialization.
+         */
+        final ScopeDeriver scopeDeriver;
+
+        /**
+         * The effective optionality selector, i.e. after initialization.
+         */
+        final OptionalitySelector optionalitySelector;
+
+        State( DependencyNode root, Map<?, ?> conflictIds, int conflictIdCount,
+               DependencyGraphTransformationContext context )
+            throws RepositoryException
+        {
+            this.conflictIds = conflictIds;
+            verbose = Maven30ConfigUtils.getBoolean( context.getSession(), false, CONFIG_PROP_VERBOSE );
+            potentialAncestorIds = new HashSet<Object>( conflictIdCount * 2 );
+            resolvedIds = new HashMap<Object, DependencyNode>( conflictIdCount * 2 );
+            items = new ArrayList<ConflictItem>( 256 );
+            infos = new IdentityHashMap<List<DependencyNode>, NodeInfo>( 64 );
+            stack = new IdentityHashMap<List<DependencyNode>, Object>( 64 );
+            parentNodes = new ArrayList<DependencyNode>( 64 );
+            parentScopes = new ArrayList<String>( 64 );
+            parentOptionals = new ArrayList<Boolean>( 64 );
+            parentInfos = new ArrayList<NodeInfo>( 64 );
+            conflictCtx = new ConflictContext( root, conflictIds, items );
+            scopeCtx = new ScopeContext( null, null );
+            versionSelector = Maven30ConflictResolver.this.versionSelector.getInstance( root, context );
+            scopeSelector = Maven30ConflictResolver.this.scopeSelector.getInstance( root, context );
+            scopeDeriver = Maven30ConflictResolver.this.scopeDeriver.getInstance( root, context );
+            optionalitySelector = Maven30ConflictResolver.this.optionalitySelector.getInstance( root, context );
+        }
+
+        void prepare( Object conflictId, Collection<Object> cyclicPredecessors )
+        {
+            currentId = conflictId;
+            conflictCtx.conflictId = conflictId;
+            conflictCtx.winner = null;
+            conflictCtx.scope = null;
+            conflictCtx.optional = null;
+            items.clear();
+            infos.clear();
+            if ( cyclicPredecessors != null )
+            {
+                potentialAncestorIds.addAll( cyclicPredecessors );
+            }
+        }
+
+        void finish()
+        {
+            List<DependencyNode> previousParent = null;
+            int previousDepth = 0;
+            totalConflictItems += items.size();
+            for ( int i = items.size() - 1; i >= 0; i-- )
+            {
+                ConflictItem item = items.get( i );
+                if ( item.parent == previousParent )
+                {
+                    item.depth = previousDepth;
+                }
+                else if ( item.parent != null )
+                {
+                    previousParent = item.parent;
+                    NodeInfo info = infos.get( previousParent );
+                    previousDepth = info.minDepth + 1;
+                    item.depth = previousDepth;
+                }
+            }
+            potentialAncestorIds.add( currentId );
+        }
+
+        void winner()
+        {
+            resolvedIds.put( currentId, ( conflictCtx.winner != null ) ? conflictCtx.winner.node : null );
+        }
+
+        boolean loser( DependencyNode node, Object conflictId )
+        {
+            DependencyNode winner = resolvedIds.get( conflictId );
+            return winner != null && winner != node;
+        }
+
+        boolean push( DependencyNode node, Object conflictId )
+            throws RepositoryException
+        {
+            if ( conflictId == null )
+            {
+                if ( node.getDependency() != null )
+                {
+                    if ( nodeData.getData( node ).get( NODE_DATA_WINNER ) != null )
+//                    if ( node.getData().get( NODE_DATA_WINNER ) != null )
+                    {
+                        return false;
+                    }
+                    throw new RepositoryException( "missing conflict id for node " + node );
+                }
+            }
+            else if ( !potentialAncestorIds.contains( conflictId ) )
+            {
+                return false;
+            }
+
+            List<DependencyNode> graphNode = node.getChildren();
+            if ( stack.put( graphNode, Boolean.TRUE ) != null )
+            {
+                return false;
+            }
+
+            int depth = depth();
+            String scope = deriveScope( node, conflictId );
+            boolean optional = deriveOptional( node, conflictId );
+            NodeInfo info = infos.get( graphNode );
+            if ( info == null )
+            {
+                info = new NodeInfo( depth, scope, optional );
+                infos.put( graphNode, info );
+                parentInfos.add( info );
+                parentNodes.add( node );
+                parentScopes.add( scope );
+                parentOptionals.add( optional );
+            }
+            else
+            {
+                int changes = info.update( depth, scope, optional );
+                if ( changes == 0 )
+                {
+                    stack.remove( graphNode );
+                    return false;
+                }
+                parentInfos.add( null ); // disable creating new conflict items, we update the existing ones below
+                parentNodes.add( node );
+                parentScopes.add( scope );
+                parentOptionals.add( optional );
+                if ( info.children != null )
+                {
+                    if ( ( changes & NodeInfo.CHANGE_SCOPE ) != 0 )
+                    {
+                        for ( int i = info.children.size() - 1; i >= 0; i-- )
+                        {
+                            ConflictItem item = info.children.get( i );
+                            String childScope = deriveScope( item.node, null );
+                            item.addScope( childScope );
+                        }
+                    }
+                    if ( ( changes & NodeInfo.CHANGE_OPTIONAL ) != 0 )
+                    {
+                        for ( int i = info.children.size() - 1; i >= 0; i-- )
+                        {
+                            ConflictItem item = info.children.get( i );
+                            boolean childOptional = deriveOptional( item.node, null );
+                            item.addOptional( childOptional );
+                        }
+                    }
+                }
+            }
+
+            return true;
+        }
+
+        void pop()
+        {
+            int last = parentInfos.size() - 1;
+            parentInfos.remove( last );
+            parentScopes.remove( last );
+            parentOptionals.remove( last );
+            DependencyNode node = parentNodes.remove( last );
+            stack.remove( node.getChildren() );
+        }
+
+        void add( DependencyNode node )
+            throws RepositoryException
+        {
+            DependencyNode parent = parent();
+            if ( parent == null )
+            {
+                ConflictItem item = newConflictItem( parent, node );
+                items.add( item );
+            }
+            else
+            {
+                NodeInfo info = parentInfos.get( parentInfos.size() - 1 );
+                if ( info != null )
+                {
+                    ConflictItem item = newConflictItem( parent, node );
+                    info.add( item );
+                    items.add( item );
+                }
+            }
+        }
+
+        private ConflictItem newConflictItem( DependencyNode parent, DependencyNode node )
+            throws RepositoryException
+        {
+            return new ConflictItem( parent, node, deriveScope( node, null ), deriveOptional( node, null ) );
+        }
+
+        private int depth()
+        {
+            return parentNodes.size();
+        }
+
+        private DependencyNode parent()
+        {
+            int size = parentNodes.size();
+            return ( size <= 0 ) ? null : parentNodes.get( size - 1 );
+        }
+
+        private String deriveScope( DependencyNode node, Object conflictId )
+            throws RepositoryException
+        {
+            if ( node.getPremanagedScope() != null || ( conflictId != null && resolvedIds.containsKey( conflictId ) ) )
+//        if ( ( node.getManagedBits() & DependencyNode.MANAGED_SCOPE ) != 0
+//            || ( conflictId != null && resolvedIds.containsKey( conflictId ) ) )
+            {
+                return scope( node.getDependency() );
+            }
+
+            int depth = parentNodes.size();
+            scopes( depth, node.getDependency() );
+            if ( depth > 0 )
+            {
+                scopeDeriver.deriveScope( scopeCtx );
+            }
+            return scopeCtx.derivedScope;
+        }
+
+        private void scopes( int parent, Dependency child )
+        {
+            scopeCtx.parentScope = ( parent > 0 ) ? parentScopes.get( parent - 1 ) : null;
+            scopeCtx.derivedScope = scope( child );
+            scopeCtx.childScope = scopeCtx.derivedScope;
+        }
+
+        private String scope( Dependency dependency )
+        {
+            return ( dependency != null ) ? dependency.getScope() : null;
+        }
+
+        private boolean deriveOptional( DependencyNode node, Object conflictId )
+        {
+            Dependency dep = node.getDependency();
+            boolean optional = ( dep != null ) ? dep.isOptional() : false;
+// sonatype aether 1.7
+            if ( optional
+                || ( nodeData.getData( node ).get( NODE_DATA_ORIGINAL_OPTIONALITY ) != null
+                    && ( (Boolean) nodeData.getData( node ).get( NODE_DATA_ORIGINAL_OPTIONALITY ) ).booleanValue() )
+                || ( conflictId != null && resolvedIds.containsKey( conflictId ) ) )
+// sonatype aether 1.13.1
+//            if ( optional
+//                || ( node.getData().get( NODE_DATA_ORIGINAL_OPTIONALITY ) != null
+//                    && ( (Boolean) node.getData().get( NODE_DATA_ORIGINAL_OPTIONALITY ) ).booleanValue() )
+//                || ( conflictId != null && resolvedIds.containsKey( conflictId ) ) )
+
+// eclipse aether 0.90
+//        if ( optional || ( node.getManagedBits() & DependencyNode.MANAGED_OPTIONAL ) != 0
+//            || ( conflictId != null && resolvedIds.containsKey( conflictId ) ) )
+            {
+                return optional;
+            }
+            int depth = parentNodes.size();
+            return ( depth > 0 ) ? parentOptionals.get( depth - 1 ) : false;
+        }
+
+    }
+
+    /**
+     * A context used to hold information that is relevant for deriving the scope of a child dependency.
+     * 
+     * @see ScopeDeriver
+     * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
+     *                change without notice and only exists to enable unit testing.
+     */
+    public static final class ScopeContext
+    {
+
+        String parentScope;
+
+        String childScope;
+
+        String derivedScope;
+
+        /**
+         * Creates a new scope context with the specified properties.
+         * 
+         * @param parentScope The scope of the parent dependency, may be {@code null}.
+         * @param childScope The scope of the child dependency, may be {@code null}.
+         * @noreference This class is not intended to be instantiated by clients in production code, the constructor may
+         *              change without notice and only exists to enable unit testing.
+         */
+        public ScopeContext( String parentScope, String childScope )
+        {
+            this.parentScope = ( parentScope != null ) ? parentScope : "";
+            derivedScope = ( childScope != null ) ? childScope : "";
+            this.childScope = derivedScope;
+        }
+
+        /**
+         * Gets the scope of the parent dependency. This is usually the scope that was derived by earlier invocations of
+         * the scope deriver.
+         * 
+         * @return The scope of the parent dependency, never {@code null}.
+         */
+        public String getParentScope()
+        {
+            return parentScope;
+        }
+
+        /**
+         * Gets the original scope of the child dependency. This is the scope that was declared in the artifact
+         * descriptor of the parent dependency.
+         * 
+         * @return The original scope of the child dependency, never {@code null}.
+         */
+        public String getChildScope()
+        {
+            return childScope;
+        }
+
+        /**
+         * Gets the derived scope of the child dependency. This is initially equal to {@link #getChildScope()} until the
+         * scope deriver makes changes.
+         * 
+         * @return The derived scope of the child dependency, never {@code null}.
+         */
+        public String getDerivedScope()
+        {
+            return derivedScope;
+        }
+
+        /**
+         * Sets the derived scope of the child dependency.
+         * 
+         * @param derivedScope The derived scope of the dependency, may be {@code null}.
+         */
+        public void setDerivedScope( String derivedScope )
+        {
+            this.derivedScope = ( derivedScope != null ) ? derivedScope : "";
+        }
+
+    }
+
+    /**
+     * A conflicting dependency.
+     * 
+     * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
+     *                change without notice and only exists to enable unit testing.
+     */
+    public static final class ConflictItem
+    {
+
+        // nodes can share child lists, we care about the unique owner of a child node which is the child list
+        final List<DependencyNode> parent;
+
+        // only for debugging/toString() to help identify the parent node(s)
+        final Artifact artifact;
+
+        final DependencyNode node;
+
+        int depth;
+
+        // we start with String and update to Set<String> if needed
+        Object scopes;
+
+        // bit field of OPTIONAL_FALSE and OPTIONAL_TRUE
+        int optionalities;
+
+        /**
+         * Bit flag indicating whether one or more paths consider the dependency non-optional.
+         */
+        public static final int OPTIONAL_FALSE = 0x01;
+
+        /**
+         * Bit flag indicating whether one or more paths consider the dependency optional.
+         */
+        public static final int OPTIONAL_TRUE = 0x02;
+
+        ConflictItem( DependencyNode parent, DependencyNode node, String scope, boolean optional )
+        {
+            if ( parent != null )
+            {
+                this.parent = parent.getChildren();
+                this.artifact = parent.getDependency().getArtifact();
+//            this.artifact = parent.getArtifact();
+            }
+            else
+            {
+                this.parent = null;
+                this.artifact = null;
+            }
+            this.node = node;
+            this.scopes = scope;
+            this.optionalities = optional ? OPTIONAL_TRUE : OPTIONAL_FALSE;
+        }
+
+        /**
+         * Creates a new conflict item with the specified properties.
+         * 
+         * @param parent The parent node of the conflicting dependency, may be {@code null}.
+         * @param node The conflicting dependency, must not be {@code null}.
+         * @param depth The zero-based depth of the conflicting dependency.
+         * @param optionalities The optionalities the dependency was encountered with, encoded as a bit field consisting
+         *            of {@link ConflictResolver.ConflictItem#OPTIONAL_TRUE} and
+         *            {@link ConflictResolver.ConflictItem#OPTIONAL_FALSE}.
+         * @param scopes The derived scopes of the conflicting dependency, must not be {@code null}.
+         * @noreference This class is not intended to be instantiated by clients in production code, the constructor may
+         *              change without notice and only exists to enable unit testing.
+         */
+        public ConflictItem( DependencyNode parent, DependencyNode node, int depth, int optionalities,
+                             String... scopes )
+        {
+            this.parent = ( parent != null ) ? parent.getChildren() : null;
+            this.artifact = ( parent != null ) ? parent.getDependency().getArtifact() : null;
+//        this.artifact = ( parent != null ) ? parent.getArtifact() : null;
+            this.node = node;
+            this.depth = depth;
+            this.optionalities = optionalities;
+            this.scopes = Arrays.asList( scopes );
+        }
+
+        /**
+         * Determines whether the specified conflict item is a sibling of this item.
+         * 
+         * @param item The other conflict item, must not be {@code null}.
+         * @return {@code true} if the given item has the same parent as this item, {@code false} otherwise.
+         */
+        public boolean isSibling( ConflictItem item )
+        {
+            return parent == item.parent;
+        }
+
+        /**
+         * Gets the dependency node involved in the conflict.
+         * 
+         * @return The involved dependency node, never {@code null}.
+         */
+        public DependencyNode getNode()
+        {
+            return node;
+        }
+
+        /**
+         * Gets the dependency involved in the conflict, short for {@code getNode.getDependency()}.
+         * 
+         * @return The involved dependency, never {@code null}.
+         */
+        public Dependency getDependency()
+        {
+            return node.getDependency();
+        }
+
+        /**
+         * Gets the zero-based depth at which the conflicting node occurs in the graph. As such, the depth denotes the
+         * number of parent nodes. If actually multiple paths lead to the node, the return value denotes the smallest
+         * possible depth.
+         * 
+         * @return The zero-based depth of the node in the graph.
+         */
+        public int getDepth()
+        {
+            return depth;
+        }
+
+        /**
+         * Gets the derived scopes of the dependency. In general, the same dependency node could be reached via
+         * different paths and each path might result in a different derived scope.
+         * 
+         * @see ScopeDeriver
+         * @return The (read-only) set of derived scopes of the dependency, never {@code null}.
+         */
+        @SuppressWarnings( "unchecked" )
+        public Collection<String> getScopes()
+        {
+            if ( scopes instanceof String )
+            {
+                return Collections.singleton( (String) scopes );
+            }
+            return (Collection<String>) scopes;
+        }
+
+        @SuppressWarnings( "unchecked" )
+        void addScope( String scope )
+        {
+            if ( scopes instanceof Collection )
+            {
+                ( (Collection<String>) scopes ).add( scope );
+            }
+            else if ( !scopes.equals( scope ) )
+            {
+                Collection<Object> set = new HashSet<Object>();
+                set.add( scopes );
+                set.add( scope );
+                scopes = set;
+            }
+        }
+
+        /**
+         * Gets the derived optionalities of the dependency. In general, the same dependency node could be reached via
+         * different paths and each path might result in a different derived optionality.
+         * 
+         * @return A bit field consisting of {@link ConflictResolver.ConflictItem#OPTIONAL_FALSE} and/or
+         *         {@link ConflictResolver.ConflictItem#OPTIONAL_TRUE} indicating the derived optionalities the
+         *         dependency was encountered with.
+         */
+        public int getOptionalities()
+        {
+            return optionalities;
+        }
+
+        void addOptional( boolean optional )
+        {
+            optionalities |= optional ? OPTIONAL_TRUE : OPTIONAL_FALSE;
+        }
+
+        @Override
+        public String toString()
+        {
+            return node + " @ " + depth + " < " + artifact;
+        }
+
+    }
+
+    /**
+     * A context used to hold information that is relevant for resolving version and scope conflicts.
+     * 
+     * @see VersionSelector
+     * @see ScopeSelector
+     * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
+     *                change without notice and only exists to enable unit testing.
+     */
+    public static final class ConflictContext
+    {
+
+        final DependencyNode root;
+
+        final Map<?, ?> conflictIds;
+
+        final Collection<ConflictItem> items;
+
+        Object conflictId;
+
+        ConflictItem winner;
+
+        String scope;
+
+        Boolean optional;
+
+        ConflictContext( DependencyNode root, Map<?, ?> conflictIds, Collection<ConflictItem> items )
+        {
+            this.root = root;
+            this.conflictIds = conflictIds;
+            this.items = Collections.unmodifiableCollection( items );
+        }
+
+        /**
+         * Creates a new conflict context.
+         * 
+         * @param root The root node of the dependency graph, must not be {@code null}.
+         * @param conflictId The conflict id for the set of conflicting dependencies in this context, must not be
+         *            {@code null}.
+         * @param conflictIds The mapping from dependency node to conflict id, must not be {@code null}.
+         * @param items The conflict items in this context, must not be {@code null}.
+         * @noreference This class is not intended to be instantiated by clients in production code, the constructor may
+         *              change without notice and only exists to enable unit testing.
+         */
+        public ConflictContext( DependencyNode root, Object conflictId, Map<DependencyNode, Object> conflictIds,
+                                Collection<ConflictItem> items )
+        {
+            this( root, conflictIds, items );
+            this.conflictId = conflictId;
+        }
+
+        /**
+         * Gets the root node of the dependency graph being transformed.
+         * 
+         * @return The root node of the dependeny graph, never {@code null}.
+         */
+        public DependencyNode getRoot()
+        {
+            return root;
+        }
+
+        /**
+         * Determines whether the specified dependency node belongs to this conflict context.
+         * 
+         * @param node The dependency node to check, must not be {@code null}.
+         * @return {@code true} if the given node belongs to this conflict context, {@code false} otherwise.
+         */
+        public boolean isIncluded( DependencyNode node )
+        {
+            return conflictId.equals( conflictIds.get( node ) );
+        }
+
+        /**
+         * Gets the collection of conflict items in this context.
+         * 
+         * @return The (read-only) collection of conflict items in this context, never {@code null}.
+         */
+        public Collection<ConflictItem> getItems()
+        {
+            return items;
+        }
+
+        /**
+         * Gets the conflict item which has been selected as the winner among the conflicting dependencies.
+         * 
+         * @return The winning conflict item or {@code null} if not set yet.
+         */
+        public ConflictItem getWinner()
+        {
+            return winner;
+        }
+
+        /**
+         * Sets the conflict item which has been selected as the winner among the conflicting dependencies.
+         * 
+         * @param winner The winning conflict item, may be {@code null}.
+         */
+        public void setWinner( ConflictItem winner )
+        {
+            this.winner = winner;
+        }
+
+        /**
+         * Gets the effective scope of the winning dependency.
+         * 
+         * @return The effective scope of the winning dependency or {@code null} if none.
+         */
+        public String getScope()
+        {
+            return scope;
+        }
+
+        /**
+         * Sets the effective scope of the winning dependency.
+         * 
+         * @param scope The effective scope, may be {@code null}.
+         */
+        public void setScope( String scope )
+        {
+            this.scope = scope;
+        }
+
+        /**
+         * Gets the effective optional flag of the winning dependency.
+         * 
+         * @return The effective optional flag or {@code null} if none.
+         */
+        public Boolean getOptional()
+        {
+            return optional;
+        }
+
+        /**
+         * Sets the effective optional flag of the winning dependency.
+         * 
+         * @param optional The effective optional flag, may be {@code null}.
+         */
+        public void setOptional( Boolean optional )
+        {
+            this.optional = optional;
+        }
+
+        @Override
+        public String toString()
+        {
+            return winner + " @ " + scope + " < " + items;
+        }
+
+    }
+
+    /**
+     * An extension point of {@link ConflictResolver} that determines the winner among conflicting dependencies. The
+     * winning node (and its children) will be retained in the dependency graph, the other nodes will get removed. The
+     * version selector does not need to deal with potential scope conflicts, these will be addressed afterwards by the
+     * {@link ScopeSelector}. Implementations must be stateless.
+     */
+    public abstract static class VersionSelector
+    {
+
+        /**
+         * Retrieves the version selector for use during the specified graph transformation. The conflict resolver calls
+         * this method once per
+         * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
+         * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
+         * implementations need to be stateless, a new instance needs to be returned to hold such auxiliary data. The
+         * default implementation simply returns the current instance which is appropriate for implementations which do
+         * not require auxiliary data.
+         * 
+         * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
+         * @param context The graph transformation context, must not be {@code null}.
+         * @return The scope deriver to use for the given graph transformation, never {@code null}.
+         * @throws RepositoryException If the instance could not be retrieved.
+         */
+        public VersionSelector getInstance( DependencyNode root, DependencyGraphTransformationContext context )
+            throws RepositoryException
+        {
+            return this;
+        }
+
+        /**
+         * Determines the winning node among conflicting dependencies. Implementations will usually iterate
+         * {@link ConflictContext#getItems()}, inspect {@link ConflictItem#getNode()} and eventually call
+         * {@link ConflictContext#setWinner(ConflictResolver.ConflictItem)} to deliver the winner. Failure to select a
+         * winner will automatically fail the entire conflict resolution.
+         * 
+         * @param context The conflict context, must not be {@code null}.
+         * @throws RepositoryException If the version selection failed.
+         */
+        public abstract void selectVersion( ConflictContext context )
+            throws RepositoryException;
+
+    }
+
+    /**
+     * An extension point of {@link ConflictResolver} that determines the effective scope of a dependency from a
+     * potentially conflicting set of {@link ScopeDeriver derived scopes}. The scope selector gets invoked after the
+     * {@link VersionSelector} has picked the winning node. Implementations must be stateless.
+     */
+    public abstract static class ScopeSelector
+    {
+
+        /**
+         * Retrieves the scope selector for use during the specified graph transformation. The conflict resolver calls
+         * this method once per
+         * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
+         * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
+         * implementations need to be stateless, a new instance needs to be returned to hold such auxiliary data. The
+         * default implementation simply returns the current instance which is appropriate for implementations which do
+         * not require auxiliary data.
+         * 
+         * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
+         * @param context The graph transformation context, must not be {@code null}.
+         * @return The scope selector to use for the given graph transformation, never {@code null}.
+         * @throws RepositoryException If the instance could not be retrieved.
+         */
+        public ScopeSelector getInstance( DependencyNode root, DependencyGraphTransformationContext context )
+            throws RepositoryException
+        {
+            return this;
+        }
+
+        /**
+         * Determines the effective scope of the dependency given by {@link ConflictContext#getWinner()}.
+         * Implementations will usually iterate {@link ConflictContext#getItems()}, inspect
+         * {@link ConflictItem#getScopes()} and eventually call {@link ConflictContext#setScope(String)} to deliver the
+         * effective scope.
+         * 
+         * @param context The conflict context, must not be {@code null}.
+         * @throws RepositoryException If the scope selection failed.
+         */
+        public abstract void selectScope( ConflictContext context )
+            throws RepositoryException;
+
+    }
+
+    /**
+     * An extension point of {@link ConflictResolver} that determines the scope of a dependency in relation to the scope
+     * of its parent. Implementations must be stateless.
+     */
+    public abstract static class ScopeDeriver
+    {
+
+        /**
+         * Retrieves the scope deriver for use during the specified graph transformation. The conflict resolver calls
+         * this method once per
+         * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
+         * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
+         * implementations need to be stateless, a new instance needs to be returned to hold such auxiliary data. The
+         * default implementation simply returns the current instance which is appropriate for implementations which do
+         * not require auxiliary data.
+         * 
+         * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
+         * @param context The graph transformation context, must not be {@code null}.
+         * @return The scope deriver to use for the given graph transformation, never {@code null}.
+         * @throws RepositoryException If the instance could not be retrieved.
+         */
+        public ScopeDeriver getInstance( DependencyNode root, DependencyGraphTransformationContext context )
+            throws RepositoryException
+        {
+            return this;
+        }
+
+        /**
+         * Determines the scope of a dependency in relation to the scope of its parent. Implementors need to call
+         * {@link ScopeContext#setDerivedScope(String)} to deliver the result of their calculation. If said method is
+         * not invoked, the conflict resolver will assume the scope of the child dependency remains unchanged.
+         * 
+         * @param context The scope context, must not be {@code null}.
+         * @throws RepositoryException If the scope deriviation failed.
+         */
+        public abstract void deriveScope( ScopeContext context )
+            throws RepositoryException;
+
+    }
+
+    /**
+     * An extension point of {@link ConflictResolver} that determines the effective optional flag of a dependency from a
+     * potentially conflicting set of derived optionalities. The optionality selector gets invoked after the
+     * {@link VersionSelector} has picked the winning node. Implementations must be stateless.
+     */
+    public abstract static class OptionalitySelector
+    {
+
+        /**
+         * Retrieves the optionality selector for use during the specified graph transformation. The conflict resolver
+         * calls this method once per
+         * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
+         * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
+         * implementations need to be stateless, a new instance needs to be returned to hold such auxiliary data. The
+         * default implementation simply returns the current instance which is appropriate for implementations which do
+         * not require auxiliary data.
+         * 
+         * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
+         * @param context The graph transformation context, must not be {@code null}.
+         * @return The optionality selector to use for the given graph transformation, never {@code null}.
+         * @throws RepositoryException If the instance could not be retrieved.
+         */
+        public OptionalitySelector getInstance( DependencyNode root, DependencyGraphTransformationContext context )
+            throws RepositoryException
+        {
+            return this;
+        }
+
+        /**
+         * Determines the effective optional flag of the dependency given by {@link ConflictContext#getWinner()}.
+         * Implementations will usually iterate {@link ConflictContext#getItems()}, inspect
+         * {@link ConflictItem#getOptionalities()} and eventually call {@link ConflictContext#setOptional(Boolean)} to
+         * deliver the effective optional flag.
+         * 
+         * @param context The conflict context, must not be {@code null}.
+         * @throws RepositoryException If the optionality selection failed.
+         */
+        public abstract void selectOptionality( ConflictContext context )
+            throws RepositoryException;
+
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DependencyCollector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DependencyCollector.java
index 4b4eaf2..a52c065 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DependencyCollector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DependencyCollector.java
@@ -25,8 +25,10 @@ import java.util.List;
 import org.apache.maven.RepositoryUtils;
 import org.apache.maven.artifact.handler.ArtifactHandler;
 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.model.Model;
-
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.shared.transfer.dependencies.DependableCoordinate;
 import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult;
 import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollector;
@@ -36,10 +38,18 @@ import org.sonatype.aether.RepositorySystemSession;
 import org.sonatype.aether.artifact.Artifact;
 import org.sonatype.aether.artifact.ArtifactTypeRegistry;
 import org.sonatype.aether.collection.CollectRequest;
+import org.sonatype.aether.collection.CollectResult;
 import org.sonatype.aether.collection.DependencyCollectionException;
+import org.sonatype.aether.collection.DependencyGraphTransformer;
+import org.sonatype.aether.collection.DependencySelector;
 import org.sonatype.aether.graph.Dependency;
 import org.sonatype.aether.repository.RemoteRepository;
+import org.sonatype.aether.util.DefaultRepositorySystemSession;
 import org.sonatype.aether.util.artifact.DefaultArtifact;
+import org.sonatype.aether.util.artifact.JavaScopes;
+import org.sonatype.aether.util.graph.selector.AndDependencySelector;
+import org.sonatype.aether.util.graph.selector.ExclusionDependencySelector;
+import org.sonatype.aether.util.graph.selector.OptionalDependencySelector;
 
 /**
  * Maven 3.0 implementation of the {@link DependencyCollector}
@@ -141,6 +151,96 @@ class Maven30DependencyCollector
         return collectDependencies( request );
     }
 
+    @Override
+    public CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+        throws DependencyCollectorException
+    {
+        try
+        {
+            MavenProject project = buildingRequest.getProject();
+
+            org.apache.maven.artifact.Artifact projectArtifact = project.getArtifact();
+            List<ArtifactRepository> remoteArtifactRepositories = project.getRemoteArtifactRepositories();
+
+            RepositorySystemSession repositorySystemSession = buildingRequest.getRepositorySession();
+
+            DefaultRepositorySystemSession session = new DefaultRepositorySystemSession( repositorySystemSession );
+
+            DependencyGraphTransformer transformer =
+                new Maven30ConflictResolver( new Maven30NearestVersionSelector(), new Maven30JavaScopeSelector(),
+                                             new Maven30SimpleOptionalitySelector(), new Maven30JavaScopeDeriver(),
+                                             new Maven30NodeData() );
+            session.setDependencyGraphTransformer( transformer );
+
+            DependencySelector depFilter =
+                new AndDependencySelector( new Maven30DirectScopeDependencySelector( JavaScopes.TEST ),
+                                           new OptionalDependencySelector(), new ExclusionDependencySelector() );
+            session.setDependencySelector( depFilter );
+
+            session.setConfigProperty( Maven30ConflictResolver.CONFIG_PROP_VERBOSE, true );
+            session.setConfigProperty( "aether.dependencyManager.verbose", true );
+
+            Artifact aetherArtifact =
+                (Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
+                                           org.apache.maven.artifact.Artifact.class, projectArtifact );
+
+            @SuppressWarnings( "unchecked" )
+            List<RemoteRepository> aetherRepos =
+                (List<RemoteRepository>) Invoker.invoke( RepositoryUtils.class, "toRepos", List.class,
+                                                         remoteArtifactRepositories );
+
+            CollectRequest collectRequest = new CollectRequest();
+            collectRequest.setRoot( new Dependency( aetherArtifact, "" ) );
+            collectRequest.setRepositories( aetherRepos );
+
+            ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
+            collectDependencyList( collectRequest, project, stereotypes );
+            collectManagedDependencyList( collectRequest, project, stereotypes );
+
+            CollectResult collectResult = repositorySystem.collectDependencies( session, collectRequest );
+
+            return new Maven30CollectorResult( collectResult );
+
+//            DependencyNode rootNode = collectResult.getRoot();
+
+//            if ( getLogger().isDebugEnabled() )
+//            {
+//                logTree( rootNode );
+//            }
+
+//            return buildDependencyNode( null, rootNode, projectArtifact, filter );
+        }
+        catch ( DependencyCollectionException e )
+        {
+            throw new DependencyCollectorException( "Could not collect dependencies: " + e.getResult(), e );
+        }
+    }
+
+    private void collectManagedDependencyList( CollectRequest collectRequest, MavenProject project,
+                                               ArtifactTypeRegistry typeRegistry )
+        throws DependencyCollectorException
+    {
+        if ( project.getDependencyManagement() != null )
+        {
+            for ( org.apache.maven.model.Dependency dependency : project.getDependencyManagement().getDependencies() )
+            {
+                Dependency aetherDep = toDependency( dependency, typeRegistry );
+                collectRequest.addManagedDependency( aetherDep );
+            }
+        }
+    }
+
+    private void collectDependencyList( CollectRequest collectRequest, MavenProject project,
+                                        ArtifactTypeRegistry typeRegistry )
+        throws DependencyCollectorException
+    {
+        for ( org.apache.maven.model.Dependency dependency : project.getDependencies() )
+        {
+            Dependency aetherDep = toDependency( dependency, typeRegistry );
+            collectRequest.addDependency( aetherDep );
+        }
+    }
+
     private CollectorResult collectDependencies( CollectRequest request )
         throws DependencyCollectorException
     {
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DirectScopeDependencySelector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DirectScopeDependencySelector.java
new file mode 100644
index 0000000..debc778
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DirectScopeDependencySelector.java
@@ -0,0 +1,130 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.sonatype.aether.collection.DependencyCollectionContext;
+import org.sonatype.aether.collection.DependencySelector;
+import org.sonatype.aether.graph.Dependency;
+
+/**
+ * A dependency selector that excludes dependencies of an specific Scope which occur beyond level one of the dependency
+ * graph.
+ * 
+ * @see {@link Dependency#getScope()}
+ * @author Gabriel Belingueres
+ */
+public class Maven30DirectScopeDependencySelector
+    implements DependencySelector
+{
+
+    private final String scope;
+
+    private final int depth;
+
+    public Maven30DirectScopeDependencySelector( String scope )
+    {
+        this( scope, 0 );
+    }
+
+    private Maven30DirectScopeDependencySelector( String scope, int depth )
+    {
+        if ( scope == null )
+        {
+            throw new IllegalArgumentException( "scope is null!" );
+        }
+        this.scope = scope;
+        this.depth = depth;
+    }
+
+    /**
+     * Decides whether the specified dependency should be included in the dependency graph.
+     * 
+     * @param dependency The dependency to check, must not be {@code null}.
+     * @return {@code false} if the dependency should be excluded from the children of the current node, {@code true}
+     *         otherwise.
+     */
+    @Override
+    public boolean selectDependency( Dependency dependency )
+    {
+        return depth < 2 || !scope.equals( dependency.getScope() );
+    }
+
+    /**
+     * Derives a dependency selector for the specified collection context. When calculating the child selector,
+     * implementors are strongly advised to simply return the current instance if nothing changed to help save memory.
+     * 
+     * @param context The dependency collection context, must not be {@code null}.
+     * @return The dependency selector for the target node, must not be {@code null}.
+     */
+    @Override
+    public DependencySelector deriveChildSelector( DependencyCollectionContext context )
+    {
+        if ( depth >= 2 )
+        {
+            return this;
+        }
+
+        return new Maven30DirectScopeDependencySelector( scope, depth + 1 );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + depth;
+        result = prime * result + ( ( scope == null ) ? 0 : scope.hashCode() );
+        return result;
+    }
+
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( this == obj )
+        {
+            return true;
+        }
+        if ( obj == null )
+        {
+            return false;
+        }
+        if ( getClass() != obj.getClass() )
+        {
+            return false;
+        }
+        Maven30DirectScopeDependencySelector other = (Maven30DirectScopeDependencySelector) obj;
+        if ( depth != other.depth )
+        {
+            return false;
+        }
+        if ( scope == null )
+        {
+            if ( other.scope != null )
+            {
+                return false;
+            }
+        }
+        else if ( !scope.equals( other.scope ) )
+        {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeDeriver.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeDeriver.java
new file mode 100644
index 0000000..70dc35d
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeDeriver.java
@@ -0,0 +1,71 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.ScopeContext;
+import org.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.ScopeDeriver;
+import org.sonatype.aether.RepositoryException;
+import org.sonatype.aether.util.artifact.JavaScopes;
+
+/**
+ * This class is a copy of their homonymous in the Eclipse Aether library, adapted to work with Sonatype Aether.
+ * 
+ * @author Gabriel Belingueres
+ */
+public class Maven30JavaScopeDeriver
+    extends ScopeDeriver
+{
+
+    @Override
+    public void deriveScope( ScopeContext context )
+        throws RepositoryException
+    {
+        context.setDerivedScope( getDerivedScope( context.getParentScope(), context.getChildScope() ) );
+    }
+
+    private String getDerivedScope( String parentScope, String childScope )
+    {
+        String derivedScope;
+
+        if ( JavaScopes.SYSTEM.equals( childScope ) || JavaScopes.TEST.equals( childScope ) )
+        {
+            derivedScope = childScope;
+        }
+        else if ( parentScope == null || parentScope.length() <= 0 || JavaScopes.COMPILE.equals( parentScope ) )
+        {
+            derivedScope = childScope;
+        }
+        else if ( JavaScopes.TEST.equals( parentScope ) || JavaScopes.RUNTIME.equals( parentScope ) )
+        {
+            derivedScope = parentScope;
+        }
+        else if ( JavaScopes.SYSTEM.equals( parentScope ) || JavaScopes.PROVIDED.equals( parentScope ) )
+        {
+            derivedScope = JavaScopes.PROVIDED;
+        }
+        else
+        {
+            derivedScope = JavaScopes.RUNTIME;
+        }
+
+        return derivedScope;
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeSelector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeSelector.java
new file mode 100644
index 0000000..f569d40
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeSelector.java
@@ -0,0 +1,100 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.ConflictContext;
+import org.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.ConflictItem;
+import org.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.ScopeSelector;
+import org.sonatype.aether.RepositoryException;
+import org.sonatype.aether.util.artifact.JavaScopes;
+
+/**
+ * This class is a copy of their homonymous in the Eclipse Aether library, adapted to work with Sonatype Aether.
+ * 
+ * @author Gabriel Belingueres
+ */
+public class Maven30JavaScopeSelector
+    extends ScopeSelector
+{
+
+    @Override
+    public void selectScope( ConflictContext context )
+        throws RepositoryException
+    {
+        String scope = context.getWinner().getDependency().getScope();
+        if ( !JavaScopes.SYSTEM.equals( scope ) )
+        {
+            scope = chooseEffectiveScope( context.getItems() );
+        }
+        context.setScope( scope );
+    }
+
+    private String chooseEffectiveScope( Collection<ConflictItem> items )
+    {
+        Set<String> scopes = new HashSet<String>();
+        for ( ConflictItem item : items )
+        {
+            if ( item.getDepth() <= 1 )
+            {
+                return item.getDependency().getScope();
+            }
+            scopes.addAll( item.getScopes() );
+        }
+        return chooseEffectiveScope( scopes );
+    }
+
+    private String chooseEffectiveScope( Set<String> scopes )
+    {
+        if ( scopes.size() > 1 )
+        {
+            scopes.remove( JavaScopes.SYSTEM );
+        }
+
+        String effectiveScope = "";
+
+        if ( scopes.size() == 1 )
+        {
+            effectiveScope = scopes.iterator().next();
+        }
+        else if ( scopes.contains( JavaScopes.COMPILE ) )
+        {
+            effectiveScope = JavaScopes.COMPILE;
+        }
+        else if ( scopes.contains( JavaScopes.RUNTIME ) )
+        {
+            effectiveScope = JavaScopes.RUNTIME;
+        }
+        else if ( scopes.contains( JavaScopes.PROVIDED ) )
+        {
+            effectiveScope = JavaScopes.PROVIDED;
+        }
+        else if ( scopes.contains( JavaScopes.TEST ) )
+        {
+            effectiveScope = JavaScopes.TEST;
+        }
+
+        return effectiveScope;
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NearestVersionSelector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NearestVersionSelector.java
new file mode 100644
index 0000000..c913c54
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NearestVersionSelector.java
@@ -0,0 +1,181 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.ConflictContext;
+import org.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.ConflictItem;
+import org.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.VersionSelector;
+import org.sonatype.aether.RepositoryException;
+import org.sonatype.aether.collection.UnsolvableVersionConflictException;
+import org.sonatype.aether.graph.DependencyFilter;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.version.Version;
+import org.sonatype.aether.version.VersionConstraint;
+
+/**
+ * This class is a copy of their homonymous in the Eclipse Aether library, adapted to work with Sonatype Aether.
+ * 
+ * @author Gabriel Belingueres
+ */
+public class Maven30NearestVersionSelector
+    extends VersionSelector
+{
+
+    @Override
+    public void selectVersion( ConflictContext context )
+        throws RepositoryException
+    {
+        ConflictGroup group = new ConflictGroup();
+        for ( ConflictItem item : context.getItems() )
+        {
+            DependencyNode node = item.getNode();
+            VersionConstraint constraint = node.getVersionConstraint();
+
+            boolean backtrack = false;
+            boolean hardConstraint = !constraint.getRanges().isEmpty();
+//        boolean hardConstraint = constraint.getRange() != null;
+
+            if ( hardConstraint )
+            {
+                if ( group.constraints.add( constraint ) )
+                {
+                    if ( group.winner != null && !constraint.containsVersion( group.winner.getNode().getVersion() ) )
+                    {
+                        backtrack = true;
+                    }
+                }
+            }
+
+            if ( isAcceptable( group, node.getVersion() ) )
+            {
+                group.candidates.add( item );
+
+                if ( backtrack )
+                {
+                    backtrack( group, context );
+                }
+                else if ( group.winner == null || isNearer( item, group.winner ) )
+                {
+                    group.winner = item;
+                }
+            }
+            else if ( backtrack )
+            {
+                backtrack( group, context );
+            }
+        }
+        context.setWinner( group.winner );
+    }
+
+    private void backtrack( ConflictGroup group, ConflictContext context )
+        throws UnsolvableVersionConflictException
+    {
+        group.winner = null;
+
+        for ( Iterator<ConflictItem> it = group.candidates.iterator(); it.hasNext(); )
+        {
+            ConflictItem candidate = it.next();
+
+            if ( !isAcceptable( group, candidate.getNode().getVersion() ) )
+            {
+                it.remove();
+            }
+            else if ( group.winner == null || isNearer( candidate, group.winner ) )
+            {
+                group.winner = candidate;
+            }
+        }
+
+        if ( group.winner == null )
+        {
+            throw newFailure( context );
+        }
+    }
+
+    private boolean isAcceptable( ConflictGroup group, Version version )
+    {
+        for ( VersionConstraint constraint : group.constraints )
+        {
+            if ( !constraint.containsVersion( version ) )
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean isNearer( ConflictItem item1, ConflictItem item2 )
+    {
+        if ( item1.isSibling( item2 ) )
+        {
+            return item1.getNode().getVersion().compareTo( item2.getNode().getVersion() ) > 0;
+        }
+        else
+        {
+            return item1.getDepth() < item2.getDepth();
+        }
+    }
+
+    private UnsolvableVersionConflictException newFailure( final ConflictContext context )
+    {
+        DependencyFilter filter = new DependencyFilter()
+        {
+            public boolean accept( DependencyNode node, List<DependencyNode> parents )
+            {
+                return context.isIncluded( node );
+            }
+        };
+        Maven30PathRecordingDependencyVisitor visitor = new Maven30PathRecordingDependencyVisitor( filter );
+        context.getRoot().accept( visitor );
+        return new UnsolvableVersionConflictException( visitor.getPaths(), null );
+//    return new UnsolvableVersionConflictException( visitor.getPaths(), context.conflictId );
+//    return new UnsolvableVersionConflictException( visitor.getPaths() );
+    }
+
+    static final class ConflictGroup
+    {
+
+        final Collection<VersionConstraint> constraints;
+
+        final Collection<ConflictItem> candidates;
+
+        ConflictItem winner;
+
+        ConflictGroup()
+        {
+            constraints = new HashSet<VersionConstraint>();
+            candidates = new ArrayList<ConflictItem>( 64 );
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.valueOf( winner );
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NodeData.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NodeData.java
new file mode 100644
index 0000000..d22f232
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NodeData.java
@@ -0,0 +1,91 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.Collections;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import org.sonatype.aether.graph.DependencyNode;
+
+/**
+ * This class replace the internal data Map lacking inside DependencyNode on earlier Sonatype Aether versions.
+ * 
+ * @author Gabriel Belingueres
+ */
+public class Maven30NodeData
+{
+
+    private IdentityHashMap<DependencyNode, Map<Object, Object>> nodeMap =
+        new IdentityHashMap<DependencyNode, Map<Object, Object>>();
+
+    public void putData( DependencyNode node, Object key, Object value )
+    {
+        Map<Object, Object> dataMap = nodeMap.get( node );
+        if ( dataMap == null )
+        {
+            dataMap = Collections.emptyMap();
+        }
+        dataMap = setData( dataMap, key, value );
+        nodeMap.put( node, dataMap );
+    }
+
+    public Map<Object, Object> getData( DependencyNode node )
+    {
+        Map<Object, Object> dataMap = nodeMap.get( node );
+        if ( dataMap == null )
+        {
+            dataMap = Collections.emptyMap();
+        }
+        return dataMap;
+    }
+
+    private Map<Object, Object> setData( Map<Object, Object> data, Object key, Object value )
+    {
+        if ( key == null )
+        {
+            throw new IllegalArgumentException( "key must not be null" );
+        }
+
+        if ( value == null )
+        {
+            if ( !data.isEmpty() )
+            {
+                data.remove( key );
+
+                if ( data.isEmpty() )
+                {
+                    data = Collections.emptyMap();
+                }
+            }
+        }
+        else
+        {
+            if ( data.isEmpty() )
+            {
+                data = new HashMap<Object, Object>();
+            }
+            data.put( key, value );
+        }
+
+        return data;
+    }
+}
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30PathRecordingDependencyVisitor.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30PathRecordingDependencyVisitor.java
new file mode 100644
index 0000000..59f98ec
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30PathRecordingDependencyVisitor.java
@@ -0,0 +1,125 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.sonatype.aether.graph.DependencyFilter;
+import org.sonatype.aether.graph.DependencyNode;
+import org.sonatype.aether.graph.DependencyVisitor;
+
+/**
+ * This class is a copy of their homonymous in the Eclipse Aether library, adapted to work with Sonatype Aether.
+ * 
+ * @author Gabriel Belingueres
+ */
+public class Maven30PathRecordingDependencyVisitor
+    implements DependencyVisitor
+{
+
+    private final DependencyFilter filter;
+
+    private final List<List<DependencyNode>> paths;
+
+    private final Maven30Stack<DependencyNode> parents;
+
+    private final boolean excludeChildrenOfMatches;
+
+    /**
+     * Creates a new visitor that uses the specified filter to identify terminal nodes of interesting paths. The visitor
+     * will not search for paths going beyond an already matched node.
+     * 
+     * @param filter The filter used to select terminal nodes of paths to record, may be {@code null} to match any node.
+     */
+    public Maven30PathRecordingDependencyVisitor( DependencyFilter filter )
+    {
+        this( filter, true );
+    }
+
+    /**
+     * Creates a new visitor that uses the specified filter to identify terminal nodes of interesting paths.
+     * 
+     * @param filter The filter used to select terminal nodes of paths to record, may be {@code null} to match any node.
+     * @param excludeChildrenOfMatches Flag controlling whether children of matched nodes should be excluded from the
+     *            traversal, thereby ignoring any potential paths to other matching nodes beneath a matching ancestor
+     *            node. If {@code true}, all recorded paths will have only one matching node (namely the terminal node),
+     *            if {@code false} a recorded path can consist of multiple matching nodes.
+     */
+    public Maven30PathRecordingDependencyVisitor( DependencyFilter filter, boolean excludeChildrenOfMatches )
+    {
+        this.filter = filter;
+        this.excludeChildrenOfMatches = excludeChildrenOfMatches;
+        this.paths = new ArrayList<List<DependencyNode>>();
+        this.parents = new Maven30Stack<DependencyNode>();
+    }
+
+    /**
+     * Gets the filter being used to select terminal nodes.
+     * 
+     * @return The filter being used or {@code null} if none.
+     */
+    public DependencyFilter getFilter()
+    {
+        return filter;
+    }
+
+    /**
+     * Gets the paths leading to nodes matching the filter that have been recorded during the graph visit. A path is
+     * given as a sequence of nodes, starting with the root node of the graph and ending with the node that matched the
+     * filter.
+     * 
+     * @return The recorded paths, never {@code null}.
+     */
+    public List<List<DependencyNode>> getPaths()
+    {
+        return paths;
+    }
+
+    public boolean visitEnter( DependencyNode node )
+    {
+        boolean accept = filter == null || filter.accept( node, parents );
+
+        parents.push( node );
+
+        if ( accept )
+        {
+            DependencyNode[] path = new DependencyNode[parents.size()];
+            int i = parents.size() - 1;
+            for ( DependencyNode n : parents )
+            {
+                path[i] = n;
+                i--;
+            }
+            paths.add( Arrays.asList( path ) );
+        }
+
+        return !( excludeChildrenOfMatches && accept );
+    }
+
+    public boolean visitLeave( DependencyNode node )
+    {
+        parents.pop();
+
+        return true;
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30SimpleOptionalitySelector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30SimpleOptionalitySelector.java
new file mode 100644
index 0000000..4335628
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30SimpleOptionalitySelector.java
@@ -0,0 +1,63 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.Collection;
+
+import org.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.ConflictContext;
+import org.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.ConflictItem;
+import org.apache.maven.shared.transfer.dependencies.collect.internal.Maven30ConflictResolver.OptionalitySelector;
+import org.sonatype.aether.RepositoryException;
+
+/**
+ * This class is a copy of their homonymous in the Eclipse Aether library, adapted to work with Sonatype Aether.
+ * 
+ * @author Gabriel Belingueres
+ */
+public class Maven30SimpleOptionalitySelector
+    extends OptionalitySelector
+{
+
+    @Override
+    public void selectOptionality( ConflictContext context )
+        throws RepositoryException
+    {
+        boolean optional = chooseEffectiveOptionality( context.getItems() );
+        context.setOptional( optional );
+    }
+
+    private boolean chooseEffectiveOptionality( Collection<ConflictItem> items )
+    {
+        boolean optional = true;
+        for ( ConflictItem item : items )
+        {
+            if ( item.getDepth() <= 1 )
+            {
+                return item.getDependency().isOptional();
+            }
+            if ( ( item.getOptionalities() & ConflictItem.OPTIONAL_FALSE ) != 0 )
+            {
+                optional = false;
+            }
+        }
+        return optional;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30Stack.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30Stack.java
new file mode 100644
index 0000000..e15373c
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30Stack.java
@@ -0,0 +1,89 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.AbstractList;
+import java.util.NoSuchElementException;
+import java.util.RandomAccess;
+
+/**
+ * This class is a copy of the Stack class in a more recent version of Sonatype Aether, since 1.7 does not have it.
+ * 
+ * @param <E> the type of the elements of the stack.
+ * @author Gabriel Belingueres
+ */
+public class Maven30Stack<E>
+    extends AbstractList<E>
+    implements RandomAccess
+{
+
+    private Object[] elements = new Object[64];
+
+    private int size;
+
+    public void push( E element )
+    {
+        if ( size >= elements.length )
+        {
+            Object[] tmp = new Object[size + 64];
+            System.arraycopy( elements, 0, tmp, 0, elements.length );
+            elements = tmp;
+        }
+        elements[size++] = element;
+    }
+
+    @SuppressWarnings( "unchecked" )
+    public E pop()
+    {
+        if ( size <= 0 )
+        {
+            throw new NoSuchElementException();
+        }
+        return (E) elements[--size];
+    }
+
+    @SuppressWarnings( "unchecked" )
+    public E peek()
+    {
+        if ( size <= 0 )
+        {
+            return null;
+        }
+        return (E) elements[size - 1];
+    }
+
+    @SuppressWarnings( "unchecked" )
+    @Override
+    public E get( int index )
+    {
+        if ( index < 0 || index >= size )
+        {
+            throw new IndexOutOfBoundsException( "Index: " + index + ", Size: " + size );
+        }
+        return (E) elements[size - index - 1];
+    }
+
+    @Override
+    public int size()
+    {
+        return size;
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31CollectorResult.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31CollectorResult.java
index 8d46390..9ff9f06 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31CollectorResult.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31CollectorResult.java
@@ -20,16 +20,25 @@ package org.apache.maven.shared.transfer.dependencies.collect.internal;
  */
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.maven.RepositoryUtils;
 import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.shared.dependency.graph.internal.DefaultDependencyNode;
 import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult;
+import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollectorException;
+import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.collection.CollectResult;
+import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.graph.DependencyVisitor;
 import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
+import org.eclipse.aether.version.VersionConstraint;
 
 /**
  * CollectorResult wrapper around {@link CollectResult}
@@ -82,4 +91,81 @@ class Maven31CollectorResult implements CollectorResult
         return mavenRepositories;
     }
 
+    @Override
+    public org.apache.maven.shared.dependency.graph.DependencyNode getDependencyGraphRoot()
+    {
+        DependencyNode root = collectResult.getRoot();
+        org.apache.maven.artifact.Artifact rootArtifact = getDependencyArtifact( root.getDependency() );
+
+        return buildDependencyNode( null, root, rootArtifact, null );
+    }
+
+    // CHECKSTYLE_OFF: LineLength
+    private org.apache.maven.shared.dependency.graph.DependencyNode buildDependencyNode( org.apache.maven.shared.dependency.graph.DependencyNode parent,
+                                                                                         DependencyNode node,
+                                                                                         org.apache.maven.artifact.Artifact artifact,
+                                                                                         ArtifactFilter filter )
+    // CHECKSTYLE_ON: LineLength
+    {
+        String premanagedVersion = DependencyManagerUtils.getPremanagedVersion( node );
+        String premanagedScope = DependencyManagerUtils.getPremanagedScope( node );
+
+        Boolean optional = null;
+        if ( node.getDependency() != null )
+        {
+            optional = node.getDependency().isOptional();
+        }
+
+        DefaultDependencyNode current =
+            new DefaultDependencyNode( parent, artifact, premanagedVersion, premanagedScope,
+                                       getVersionSelectedFromRange( node.getVersionConstraint() ), optional );
+
+        List<org.apache.maven.shared.dependency.graph.DependencyNode> nodes =
+            new ArrayList<org.apache.maven.shared.dependency.graph.DependencyNode>( node.getChildren().size() );
+        for ( DependencyNode child : node.getChildren() )
+        {
+            org.apache.maven.artifact.Artifact childArtifact = getDependencyArtifact( child.getDependency() );
+
+            if ( ( filter == null ) || filter.include( childArtifact ) )
+            {
+                nodes.add( buildDependencyNode( current, child, childArtifact, filter ) );
+            }
+        }
+
+        current.setChildren( Collections.unmodifiableList( nodes ) );
+
+        return current;
+    }
+
+    private String getVersionSelectedFromRange( VersionConstraint constraint )
+    {
+        if ( ( constraint == null ) || ( constraint.getVersion() != null ) )
+        {
+            return null;
+        }
+
+        return constraint.getRange().toString();
+    }
+
+    private org.apache.maven.artifact.Artifact getDependencyArtifact( Dependency dep )
+    {
+        Artifact artifact = dep.getArtifact();
+
+        try
+        {
+            org.apache.maven.artifact.Artifact mavenArtifact =
+                (org.apache.maven.artifact.Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
+                                                                org.eclipse.aether.artifact.Artifact.class, artifact );
+
+            mavenArtifact.setScope( dep.getScope() );
+            mavenArtifact.setOptional( dep.isOptional() );
+
+            return mavenArtifact;
+        }
+        catch ( DependencyCollectorException e )
+        {
+            // ReflectionException should not happen
+            throw new RuntimeException( e.getMessage(), e );
+        }
+    }
 }
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DependencyCollector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DependencyCollector.java
index 0ee0385..e557a1e 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DependencyCollector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DependencyCollector.java
@@ -25,20 +25,37 @@ import java.util.List;
 import org.apache.maven.RepositoryUtils;
 import org.apache.maven.artifact.handler.ArtifactHandler;
 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.model.Model;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.shared.transfer.dependencies.DependableCoordinate;
 import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult;
 import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollector;
 import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollectorException;
+import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.RepositorySystem;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.artifact.ArtifactTypeRegistry;
 import org.eclipse.aether.artifact.DefaultArtifact;
 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.DependencySelector;
 import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.util.artifact.JavaScopes;
+import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
+import org.eclipse.aether.util.graph.selector.AndDependencySelector;
+import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
+import org.eclipse.aether.util.graph.selector.OptionalDependencySelector;
+import org.eclipse.aether.util.graph.transformer.ConflictResolver;
+import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
+import org.eclipse.aether.util.graph.transformer.JavaScopeSelector;
+import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
+import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
 
 /**
  * Maven 3.1+ implementation of the {@link DependencyCollector}
@@ -155,6 +172,105 @@ class Maven31DependencyCollector
         }
     }
 
+    @Override
+    public CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+        throws DependencyCollectorException
+    {
+        DefaultRepositorySystemSession session = null;
+        try
+        {
+            MavenProject project = buildingRequest.getProject();
+
+            org.apache.maven.artifact.Artifact projectArtifact = project.getArtifact();
+            List<ArtifactRepository> remoteArtifactRepositories = project.getRemoteArtifactRepositories();
+
+            DefaultRepositorySystemSession repositorySession =
+                (DefaultRepositorySystemSession) Invoker.invoke( buildingRequest, "getRepositorySession" );
+
+            session = new DefaultRepositorySystemSession( repositorySession );
+
+            DependencyGraphTransformer transformer =
+                new ConflictResolver( new NearestVersionSelector(), new JavaScopeSelector(),
+                                      new SimpleOptionalitySelector(), new JavaScopeDeriver() );
+            session.setDependencyGraphTransformer( transformer );
+
+            DependencySelector depFilter =
+                new AndDependencySelector( new Maven31DirectScopeDependencySelector( JavaScopes.TEST ),
+                                           new OptionalDependencySelector(), new ExclusionDependencySelector() );
+            session.setDependencySelector( depFilter );
+
+            session.setConfigProperty( ConflictResolver.CONFIG_PROP_VERBOSE, true );
+            session.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, true );
+
+            Artifact aetherArtifact =
+                (Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
+                                           org.apache.maven.artifact.Artifact.class, projectArtifact );
+
+            @SuppressWarnings( "unchecked" )
+            List<org.eclipse.aether.repository.RemoteRepository> aetherRepos =
+                (List<org.eclipse.aether.repository.RemoteRepository>) Invoker.invoke( RepositoryUtils.class, "toRepos",
+                                                                                       List.class,
+                                                                                       remoteArtifactRepositories );
+
+            CollectRequest collectRequest = new CollectRequest();
+            collectRequest.setRoot( new org.eclipse.aether.graph.Dependency( aetherArtifact, "" ) );
+            collectRequest.setRepositories( aetherRepos );
+
+            org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
+            collectDependencyList( collectRequest, project, stereotypes );
+            collectManagedDependencyList( collectRequest, project, stereotypes );
+
+            CollectResult collectResult = repositorySystem.collectDependencies( session, collectRequest );
+
+            return new Maven31CollectorResult( collectResult );
+
+//            org.eclipse.aether.graph.DependencyNode rootNode = collectResult.getRoot();
+
+//            if ( getLogger().isDebugEnabled() )
+//            {
+//                logTree( rootNode );
+//            }
+
+//            return buildDependencyNode( null, rootNode, projectArtifact, filter );
+        }
+        catch ( DependencyCollectionException e )
+        {
+            throw new DependencyCollectorException( "Could not collect dependencies: " + e.getResult(), e );
+        }
+        finally
+        {
+            if ( session != null )
+            {
+                session.setReadOnly();
+            }
+        }
+    }
+
+    private void collectManagedDependencyList( CollectRequest collectRequest, MavenProject project,
+                                               ArtifactTypeRegistry typeRegistry )
+        throws DependencyCollectorException
+    {
+        if ( project.getDependencyManagement() != null )
+        {
+            for ( org.apache.maven.model.Dependency dependency : project.getDependencyManagement().getDependencies() )
+            {
+                Dependency aetherDep = toDependency( dependency, typeRegistry );
+                collectRequest.addManagedDependency( aetherDep );
+            }
+        }
+    }
+
+    private void collectDependencyList( CollectRequest collectRequest, MavenProject project,
+                                        org.eclipse.aether.artifact.ArtifactTypeRegistry typeRegistry )
+        throws DependencyCollectorException
+    {
+        for ( org.apache.maven.model.Dependency dependency : project.getDependencies() )
+        {
+            Dependency aetherDep = toDependency( dependency, typeRegistry );
+            collectRequest.addDependency( aetherDep );
+        }
+    }
+
     private static Dependency toDependency( org.apache.maven.model.Dependency root, ArtifactTypeRegistry typeRegistry )
                     throws DependencyCollectorException
     {
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DirectScopeDependencySelector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DirectScopeDependencySelector.java
new file mode 100644
index 0000000..e930597
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DirectScopeDependencySelector.java
@@ -0,0 +1,131 @@
+package org.apache.maven.shared.transfer.dependencies.collect.internal;
+
+/*
+ * 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.collection.DependencyCollectionContext;
+import org.eclipse.aether.collection.DependencySelector;
+import org.eclipse.aether.graph.Dependency;
+
+/**
+ * A dependency selector that excludes dependencies of an specific Scope which occur beyond level one of the dependency
+ * graph.
+ * 
+ * @see {@link Dependency#getScope()}
+ * @author Gabriel Belingueres
+ */
+public class Maven31DirectScopeDependencySelector
+    implements DependencySelector
+{
+
+    private final String scope;
+
+    private final int depth;
+
+    public Maven31DirectScopeDependencySelector( String scope )
+    {
+        this( scope, 0 );
+    }
+
+    private Maven31DirectScopeDependencySelector( String scope, int depth )
+    {
+        if ( scope == null )
+        {
+            throw new IllegalArgumentException( "scope is null!" );
+        }
+        this.scope = scope;
+        this.depth = depth;
+    }
+
+    /**
+     * Decides whether the specified dependency should be included in the dependency graph.
+     * 
+     * @param dependency The dependency to check, must not be {@code null}.
+     * @return {@code false} if the dependency should be excluded from the children of the current node, {@code true}
+     *         otherwise.
+     */
+    @Override
+    public boolean selectDependency( Dependency dependency )
+    {
+        return depth < 2 || !scope.equals( dependency.getScope() );
+    }
+
+    /**
+     * Derives a dependency selector for the specified collection context. When calculating the child selector,
+     * implementors are strongly advised to simply return the current instance if nothing changed to help save memory.
+     * 
+     * @param context The dependency collection context, must not be {@code null}.
+     * @return The dependency selector for the target node, must not be {@code null}.
+     */
+    @Override
+    public DependencySelector deriveChildSelector( DependencyCollectionContext context )
+    {
+        if ( depth >= 2 )
+        {
+            return this;
+        }
+
+        return new Maven31DirectScopeDependencySelector( scope, depth + 1 );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + depth;
+        result = prime * result + ( ( scope == null ) ? 0 : scope.hashCode() );
+        return result;
+    }
+
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( this == obj )
+        {
+            return true;
+        }
+        if ( obj == null )
+        {
+            return false;
+        }
+        if ( getClass() != obj.getClass() )
+        {
+            return false;
+        }
+        Maven31DirectScopeDependencySelector other = (Maven31DirectScopeDependencySelector) obj;
+        if ( depth != other.depth )
+        {
+            return false;
+        }
+        if ( scope == null )
+        {
+            if ( other.scope != null )
+            {
+                return false;
+            }
+        }
+        else if ( !scope.equals( other.scope ) )
+        {
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/MavenDependencyCollector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/MavenDependencyCollector.java
index 53823f8..ff8c314 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/MavenDependencyCollector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/MavenDependencyCollector.java
@@ -21,6 +21,7 @@ package org.apache.maven.shared.transfer.dependencies.collect.internal;
 
 import org.apache.maven.model.Dependency;
 import org.apache.maven.model.Model;
+import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.shared.transfer.dependencies.DependableCoordinate;
 import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult;
 import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollectorException;
@@ -41,4 +42,7 @@ public interface MavenDependencyCollector
     CollectorResult collectDependencies( Model root )
         throws DependencyCollectorException;
 
+    CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+        throws DependencyCollectorException;
+
 }


[maven-artifact-transfer] 02/02: [MSHARED-801] Add functionality to collect raw dependencies in Maven 3+

Posted by rf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rfscholte pushed a commit to branch MSHARED-801
in repository https://gitbox.apache.org/repos/asf/maven-artifact-transfer.git

commit c3edb9582d171e2c2c149d8551f61b30c9e1168b
Author: rfscholte <rf...@apache.org>
AuthorDate: Sun Nov 10 15:05:12 2019 +0100

    [MSHARED-801] Add functionality to collect raw dependencies in Maven 3+
---
 .../collector/DependencyCollectorMojo.java         |   2 +-
 .../dependencies/collect/DependencyCollector.java  |  26 +++-
 .../internal/DefaultDependencyCollector.java       |  46 ++++--
 .../collect/internal/Maven30ConflictIdSorter.java  |   4 +-
 .../collect/internal/Maven30ConflictResolver.java  |  31 ++--
 .../internal/Maven30DependencyCollector.java       | 166 +++++++++++----------
 .../Maven30DirectScopeDependencySelector.java      |  32 ++--
 .../collect/internal/Maven30JavaScopeDeriver.java  |   2 +-
 .../collect/internal/Maven30JavaScopeSelector.java |   2 +-
 .../internal/Maven30NearestVersionSelector.java    |   5 +-
 .../collect/internal/Maven30NodeData.java          |   7 +-
 .../Maven30PathRecordingDependencyVisitor.java     |   8 +-
 .../internal/Maven30SimpleOptionalitySelector.java |   2 +-
 .../collect/internal/Maven30Stack.java             |   2 +-
 .../internal/Maven31DependencyCollector.java       | 145 +++++++++---------
 .../Maven31DirectScopeDependencySelector.java      |  30 ++--
 .../collect/internal/MavenDependencyCollector.java |   9 +-
 17 files changed, 283 insertions(+), 236 deletions(-)

diff --git a/src/it/maven-dependency-collector-plugin/src/main/java/org/apache/maven/plugin/dependency/collector/DependencyCollectorMojo.java b/src/it/maven-dependency-collector-plugin/src/main/java/org/apache/maven/plugin/dependency/collector/DependencyCollectorMojo.java
index 45ab470..5907511 100644
--- a/src/it/maven-dependency-collector-plugin/src/main/java/org/apache/maven/plugin/dependency/collector/DependencyCollectorMojo.java
+++ b/src/it/maven-dependency-collector-plugin/src/main/java/org/apache/maven/plugin/dependency/collector/DependencyCollectorMojo.java
@@ -92,7 +92,7 @@ public class DependencyCollectorMojo
     {
         try
         {
-            CollectorResult result = dependencyCollector.collectDependenciesGraph( buildingRequest );
+            CollectorResult result = dependencyCollector.collectDependenciesGraph( buildingRequest, project.getModel() );
             DependencyNode root = result.getDependencyGraphRoot();
 
             StringWriter writer = new StringWriter();
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/DependencyCollector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/DependencyCollector.java
index 700d66c..325855a 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/DependencyCollector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/DependencyCollector.java
@@ -72,12 +72,36 @@ public interface DependencyCollector
 
     /**
      * @param buildingRequest {@link ProjectBuildingRequest}.
+     * @param root {@link Dependency}
+     * @return {@link CollectorResult}
+     * @throws DependencyCollectorException in case of an error which can be a component lookup error or an error while
+     *             trying to collect the dependencies.
+     * @throws IllegalArgumentException in case of parameter <code>buildingRequest</code> is <code>null</code>
+     */
+    CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest, Dependency root )
+        throws DependencyCollectorException;
+
+    /**
+     * @param buildingRequest {@link ProjectBuildingRequest}.
+     * @param root {@link DependableCoordinate}
+     * @return {@link CollectorResult}
+     * @throws DependencyCollectorException in case of an error which can be a component lookup error or an error while
+     *             trying to collect the dependencies.
+     * @throws IllegalArgumentException in case of parameter <code>buildingRequest</code> is <code>null</code>
+     */
+    CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest, DependableCoordinate root )
+        throws DependencyCollectorException;
+
+    /**
+     * @param buildingRequest {@link ProjectBuildingRequest}.
+     * @param root {@link Model}
      * @return {@link CollectorResult}
      * @throws DependencyCollectorException in case of an error which can be a component lookup error or an error while
      *             trying to collect the dependencies.
      * @throws IllegalArgumentException in case of parameter <code>buildingRequest</code> is <code>null</code>
      */
-    CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+    CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest, Model root )
         throws DependencyCollectorException;
 
+    
 }
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/DefaultDependencyCollector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/DefaultDependencyCollector.java
index 90548c3..c239839 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/DefaultDependencyCollector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/DefaultDependencyCollector.java
@@ -56,11 +56,7 @@ class DefaultDependencyCollector implements DependencyCollector, Contextualizabl
 
         try
         {
-            String hint = isMaven31() ? "maven31" : "maven3";
-
-            DependencyCollector effectiveDependencyCollector = container.lookup( DependencyCollector.class, hint );
-
-            return effectiveDependencyCollector.collectDependencies( buildingRequest, root );
+            return getMavenDependencyCollector( buildingRequest ).collectDependencies( root );
         }
         catch ( ComponentLookupException e )
         {
@@ -92,11 +88,7 @@ class DefaultDependencyCollector implements DependencyCollector, Contextualizabl
 
         try
         {
-            String hint = isMaven31() ? "maven31" : "maven3";
-
-            DependencyCollector effectiveDependencyCollector = container.lookup( DependencyCollector.class, hint );
-
-            return effectiveDependencyCollector.collectDependencies( buildingRequest, root );
+            return getMavenDependencyCollector( buildingRequest ).collectDependencies( root );
         }
         catch ( ComponentLookupException e )
         {
@@ -105,13 +97,43 @@ class DefaultDependencyCollector implements DependencyCollector, Contextualizabl
     }
 
     @Override
-    public CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+    public CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest, Model root )
+        throws DependencyCollectorException
+    {
+        validateBuildingRequest( buildingRequest );
+        try
+        {
+            return getMavenDependencyCollector( buildingRequest ).collectDependenciesGraph( root );
+        }
+        catch ( ComponentLookupException e )
+        {
+            throw new DependencyCollectorException( e.getMessage(), e );
+        }
+    }
+    
+    @Override
+    public CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest, DependableCoordinate root )
+        throws DependencyCollectorException
+    {
+        validateBuildingRequest( buildingRequest );
+        try
+        {
+            return getMavenDependencyCollector( buildingRequest ).collectDependenciesGraph( root );
+        }
+        catch ( ComponentLookupException e )
+        {
+            throw new DependencyCollectorException( e.getMessage(), e );
+        }
+    }
+    
+    @Override
+    public CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest, Dependency root )
         throws DependencyCollectorException
     {
         validateBuildingRequest( buildingRequest );
         try
         {
-            return getMavenDependencyCollector( buildingRequest ).collectDependenciesGraph( buildingRequest );
+            return getMavenDependencyCollector( buildingRequest ).collectDependenciesGraph( root );
         }
         catch ( ComponentLookupException e )
         {
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictIdSorter.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictIdSorter.java
index f8ea6de..71654d2 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictIdSorter.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictIdSorter.java
@@ -41,10 +41,10 @@ import org.sonatype.aether.util.graph.transformer.TransformationContextKeys;
  * 
  * @author Gabriel Belingueres
  */
-public class Maven30ConflictIdSorter
+class Maven30ConflictIdSorter
     implements DependencyGraphTransformer
 {
-
+    @Override
     public DependencyNode transformGraph( DependencyNode node, DependencyGraphTransformationContext context )
         throws RepositoryException
     {
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictResolver.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictResolver.java
index 4ac601a..8782232 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictResolver.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30ConflictResolver.java
@@ -47,7 +47,7 @@ import org.sonatype.aether.util.graph.transformer.TransformationContextKeys;
  * 
  * @author Gabriel Belingueres
  */
-public class Maven30ConflictResolver
+class Maven30ConflictResolver
     implements DependencyGraphTransformer
 {
 
@@ -104,7 +104,7 @@ public class Maven30ConflictResolver
      * @param nodeData the object where to save node data since Sonatype Aether 1.7 does not have that info inside the
      *            DependencyNode.
      */
-    public Maven30ConflictResolver( VersionSelector versionSelector, ScopeSelector scopeSelector,
+    Maven30ConflictResolver( VersionSelector versionSelector, ScopeSelector scopeSelector,
                                     OptionalitySelector optionalitySelector, ScopeDeriver scopeDeriver,
                                     Maven30NodeData nodeData )
     {
@@ -131,6 +131,7 @@ public class Maven30ConflictResolver
         this.nodeData = nodeData;
     }
 
+    @Override
     public DependencyNode transformGraph( DependencyNode node, DependencyGraphTransformationContext context )
         throws RepositoryException
     {
@@ -161,7 +162,7 @@ public class Maven30ConflictResolver
             throw new RepositoryException( "conflict groups have not been identified" );
         }
 
-        Map<Object, Collection<Object>> cyclicPredecessors = new HashMap<Object, Collection<Object>>();
+        Map<Object, Collection<Object>> cyclicPredecessors = new HashMap<>();
         for ( Collection<?> cycle : conflictIdCycles )
         {
             for ( Object conflictId : cycle )
@@ -304,11 +305,7 @@ public class Maven30ConflictResolver
                         nodeData.putData( loser, NODE_DATA_WINNER, winner.node );
                         nodeData.putData( loser, NODE_DATA_ORIGINAL_SCOPE, loser.getDependency().getScope() );
                         nodeData.putData( loser, NODE_DATA_ORIGINAL_OPTIONALITY, loser.getDependency().isOptional() );
-//                        loser.setData( NODE_DATA_WINNER, winner.node );
-//                        loser.setData( NODE_DATA_ORIGINAL_SCOPE, loser.getDependency().getScope() );
-//                        loser.setData( NODE_DATA_ORIGINAL_OPTIONALITY, loser.getDependency().isOptional() );
                         loser.setScope( item.getScopes().iterator().next() );
-//                    loser.setChildren( Collections.<DependencyNode>emptyList() );
                         childIt.set( loser );
                     }
                     else
@@ -778,7 +775,7 @@ public class Maven30ConflictResolver
      * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
      *                change without notice and only exists to enable unit testing.
      */
-    public static final class ScopeContext
+    static final class ScopeContext
     {
 
         String parentScope;
@@ -795,7 +792,7 @@ public class Maven30ConflictResolver
          * @noreference This class is not intended to be instantiated by clients in production code, the constructor may
          *              change without notice and only exists to enable unit testing.
          */
-        public ScopeContext( String parentScope, String childScope )
+        ScopeContext( String parentScope, String childScope )
         {
             this.parentScope = ( parentScope != null ) ? parentScope : "";
             derivedScope = ( childScope != null ) ? childScope : "";
@@ -853,7 +850,7 @@ public class Maven30ConflictResolver
      * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
      *                change without notice and only exists to enable unit testing.
      */
-    public static final class ConflictItem
+    static final class ConflictItem
     {
 
         // nodes can share child lists, we care about the unique owner of a child node which is the child list
@@ -913,7 +910,7 @@ public class Maven30ConflictResolver
          * @noreference This class is not intended to be instantiated by clients in production code, the constructor may
          *              change without notice and only exists to enable unit testing.
          */
-        public ConflictItem( DependencyNode parent, DependencyNode node, int depth, int optionalities,
+        ConflictItem( DependencyNode parent, DependencyNode node, int depth, int optionalities,
                              String... scopes )
         {
             this.parent = ( parent != null ) ? parent.getChildren() : null;
@@ -1035,7 +1032,7 @@ public class Maven30ConflictResolver
      * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
      *                change without notice and only exists to enable unit testing.
      */
-    public static final class ConflictContext
+    static final class ConflictContext
     {
 
         final DependencyNode root;
@@ -1070,7 +1067,7 @@ public class Maven30ConflictResolver
          * @noreference This class is not intended to be instantiated by clients in production code, the constructor may
          *              change without notice and only exists to enable unit testing.
          */
-        public ConflictContext( DependencyNode root, Object conflictId, Map<DependencyNode, Object> conflictIds,
+        ConflictContext( DependencyNode root, Object conflictId, Map<DependencyNode, Object> conflictIds,
                                 Collection<ConflictItem> items )
         {
             this( root, conflictIds, items );
@@ -1182,7 +1179,7 @@ public class Maven30ConflictResolver
      * version selector does not need to deal with potential scope conflicts, these will be addressed afterwards by the
      * {@link ScopeSelector}. Implementations must be stateless.
      */
-    public abstract static class VersionSelector
+    abstract static class VersionSelector
     {
 
         /**
@@ -1224,7 +1221,7 @@ public class Maven30ConflictResolver
      * potentially conflicting set of {@link ScopeDeriver derived scopes}. The scope selector gets invoked after the
      * {@link VersionSelector} has picked the winning node. Implementations must be stateless.
      */
-    public abstract static class ScopeSelector
+    abstract static class ScopeSelector
     {
 
         /**
@@ -1265,7 +1262,7 @@ public class Maven30ConflictResolver
      * An extension point of {@link ConflictResolver} that determines the scope of a dependency in relation to the scope
      * of its parent. Implementations must be stateless.
      */
-    public abstract static class ScopeDeriver
+    abstract static class ScopeDeriver
     {
 
         /**
@@ -1306,7 +1303,7 @@ public class Maven30ConflictResolver
      * potentially conflicting set of derived optionalities. The optionality selector gets invoked after the
      * {@link VersionSelector} has picked the winning node. Implementations must be stateless.
      */
-    public abstract static class OptionalitySelector
+    abstract static class OptionalitySelector
     {
 
         /**
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DependencyCollector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DependencyCollector.java
index a52c065..7ef51a3 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DependencyCollector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DependencyCollector.java
@@ -25,10 +25,7 @@ import java.util.List;
 import org.apache.maven.RepositoryUtils;
 import org.apache.maven.artifact.handler.ArtifactHandler;
 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
-import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.model.Model;
-import org.apache.maven.project.MavenProject;
-import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.shared.transfer.dependencies.DependableCoordinate;
 import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult;
 import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollector;
@@ -152,95 +149,78 @@ class Maven30DependencyCollector
     }
 
     @Override
-    public CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+    public CollectorResult collectDependenciesGraph( org.apache.maven.model.Dependency root )
         throws DependencyCollectorException
     {
-        try
-        {
-            MavenProject project = buildingRequest.getProject();
-
-            org.apache.maven.artifact.Artifact projectArtifact = project.getArtifact();
-            List<ArtifactRepository> remoteArtifactRepositories = project.getRemoteArtifactRepositories();
-
-            RepositorySystemSession repositorySystemSession = buildingRequest.getRepositorySession();
-
-            DefaultRepositorySystemSession session = new DefaultRepositorySystemSession( repositorySystemSession );
-
-            DependencyGraphTransformer transformer =
-                new Maven30ConflictResolver( new Maven30NearestVersionSelector(), new Maven30JavaScopeSelector(),
-                                             new Maven30SimpleOptionalitySelector(), new Maven30JavaScopeDeriver(),
-                                             new Maven30NodeData() );
-            session.setDependencyGraphTransformer( transformer );
-
-            DependencySelector depFilter =
-                new AndDependencySelector( new Maven30DirectScopeDependencySelector( JavaScopes.TEST ),
-                                           new OptionalDependencySelector(), new ExclusionDependencySelector() );
-            session.setDependencySelector( depFilter );
-
-            session.setConfigProperty( Maven30ConflictResolver.CONFIG_PROP_VERBOSE, true );
-            session.setConfigProperty( "aether.dependencyManager.verbose", true );
-
-            Artifact aetherArtifact =
-                (Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
-                                           org.apache.maven.artifact.Artifact.class, projectArtifact );
-
-            @SuppressWarnings( "unchecked" )
-            List<RemoteRepository> aetherRepos =
-                (List<RemoteRepository>) Invoker.invoke( RepositoryUtils.class, "toRepos", List.class,
-                                                         remoteArtifactRepositories );
-
-            CollectRequest collectRequest = new CollectRequest();
-            collectRequest.setRoot( new Dependency( aetherArtifact, "" ) );
-            collectRequest.setRepositories( aetherRepos );
-
-            ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
-            collectDependencyList( collectRequest, project, stereotypes );
-            collectManagedDependencyList( collectRequest, project, stereotypes );
+        ArtifactTypeRegistry typeRegistry =
+                        (ArtifactTypeRegistry) Invoker.invoke( RepositoryUtils.class, "newArtifactTypeRegistry",
+                                                               ArtifactHandlerManager.class, artifactHandlerManager );
 
-            CollectResult collectResult = repositorySystem.collectDependencies( session, collectRequest );
+        CollectRequest request = new CollectRequest();
+        request.setRoot( toDependency( root, typeRegistry ) );
 
-            return new Maven30CollectorResult( collectResult );
+        return collectDependenciesGraph( request );
+    }
+    
+    @Override
+    public CollectorResult collectDependenciesGraph( DependableCoordinate root )
+        throws DependencyCollectorException
+    {
+        ArtifactHandler artifactHandler = artifactHandlerManager.getArtifactHandler( root.getType() );
+        
+        String extension = artifactHandler != null ? artifactHandler.getExtension() : null;
+        
+        Artifact aetherArtifact = new DefaultArtifact( root.getGroupId(), root.getArtifactId(), root.getClassifier(),
+                                                       extension, root.getVersion() );
+        
+        CollectRequest request = new CollectRequest();
+        request.setRoot( new Dependency( aetherArtifact, null ) );
 
-//            DependencyNode rootNode = collectResult.getRoot();
+        return collectDependenciesGraph( request );
+    }
+    
+    @Override
+    public CollectorResult collectDependenciesGraph( Model root )
+        throws DependencyCollectorException
+    {
+        // Are there examples where packaging and type are NOT in sync
+        ArtifactHandler artifactHandler = artifactHandlerManager.getArtifactHandler( root.getPackaging() );
+        
+        String extension = artifactHandler != null ? artifactHandler.getExtension() : null;
+        
+        Artifact aetherArtifact =
+            new DefaultArtifact( root.getGroupId(), root.getArtifactId(), extension, root.getVersion() );
+        
+        CollectRequest request = new CollectRequest();
+        request.setRoot( new Dependency( aetherArtifact, null ) );
 
-//            if ( getLogger().isDebugEnabled() )
-//            {
-//                logTree( rootNode );
-//            }
+        ArtifactTypeRegistry typeRegistry =
+                        (ArtifactTypeRegistry) Invoker.invoke( RepositoryUtils.class, "newArtifactTypeRegistry",
+                                                               ArtifactHandlerManager.class, artifactHandlerManager );
 
-//            return buildDependencyNode( null, rootNode, projectArtifact, filter );
-        }
-        catch ( DependencyCollectionException e )
+        List<Dependency> aetherDependencies = new ArrayList<Dependency>( root.getDependencies().size() );
+        for ( org.apache.maven.model.Dependency mavenDependency : root.getDependencies() )
         {
-            throw new DependencyCollectorException( "Could not collect dependencies: " + e.getResult(), e );
+            aetherDependencies.add( toDependency( mavenDependency, typeRegistry ) );
         }
-    }
+        request.setDependencies( aetherDependencies );
 
-    private void collectManagedDependencyList( CollectRequest collectRequest, MavenProject project,
-                                               ArtifactTypeRegistry typeRegistry )
-        throws DependencyCollectorException
-    {
-        if ( project.getDependencyManagement() != null )
+        if ( root.getDependencyManagement() != null )
         {
-            for ( org.apache.maven.model.Dependency dependency : project.getDependencyManagement().getDependencies() )
+            List<Dependency> aetherManagerDependencies =
+                new ArrayList<Dependency>( root.getDependencyManagement().getDependencies().size() );
+            
+            for ( org.apache.maven.model.Dependency mavenDependency : root.getDependencyManagement().getDependencies() )
             {
-                Dependency aetherDep = toDependency( dependency, typeRegistry );
-                collectRequest.addManagedDependency( aetherDep );
+                aetherManagerDependencies.add( toDependency( mavenDependency, typeRegistry ) );
             }
+            
+            request.setManagedDependencies( aetherManagerDependencies );
         }
+        
+        return collectDependenciesGraph( request );
     }
-
-    private void collectDependencyList( CollectRequest collectRequest, MavenProject project,
-                                        ArtifactTypeRegistry typeRegistry )
-        throws DependencyCollectorException
-    {
-        for ( org.apache.maven.model.Dependency dependency : project.getDependencies() )
-        {
-            Dependency aetherDep = toDependency( dependency, typeRegistry );
-            collectRequest.addDependency( aetherDep );
-        }
-    }
-
+    
     private CollectorResult collectDependencies( CollectRequest request )
         throws DependencyCollectorException
     {
@@ -255,6 +235,38 @@ class Maven30DependencyCollector
             throw new DependencyCollectorException( e.getMessage(), e );
         }
     }
+    
+    private CollectorResult collectDependenciesGraph( CollectRequest request )
+                    throws DependencyCollectorException
+    {
+        DefaultRepositorySystemSession session = new DefaultRepositorySystemSession( this.session );
+        try
+        {
+            DependencyGraphTransformer transformer =
+                new Maven30ConflictResolver( new Maven30NearestVersionSelector(), new Maven30JavaScopeSelector(),
+                                             new Maven30SimpleOptionalitySelector(), new Maven30JavaScopeDeriver(),
+                                             new Maven30NodeData() );
+            session.setDependencyGraphTransformer( transformer );
+
+            DependencySelector depFilter =
+                new AndDependencySelector( new Maven30DirectScopeDependencySelector( JavaScopes.TEST ),
+                                           new OptionalDependencySelector(), new ExclusionDependencySelector() );
+            session.setDependencySelector( depFilter );
+
+            session.setConfigProperty( Maven30ConflictResolver.CONFIG_PROP_VERBOSE, true );
+            session.setConfigProperty( "aether.dependencyManager.verbose", true );
+
+            request.setRepositories( aetherRepositories );
+
+            CollectResult collectResult = repositorySystem.collectDependencies( session, request );
+
+            return new Maven30CollectorResult( collectResult );
+        }
+        catch ( DependencyCollectionException e )
+        {
+            throw new DependencyCollectorException( "Could not collect dependencies: " + e.getResult(), e );
+        }
+    }
 
     private static Dependency toDependency( org.apache.maven.model.Dependency mavenDependency,
                                             ArtifactTypeRegistry typeRegistry )
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DirectScopeDependencySelector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DirectScopeDependencySelector.java
index debc778..116fa75 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DirectScopeDependencySelector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30DirectScopeDependencySelector.java
@@ -19,6 +19,8 @@ package org.apache.maven.shared.transfer.dependencies.collect.internal;
  * under the License.
  */
 
+import java.util.Objects;
+
 import org.sonatype.aether.collection.DependencyCollectionContext;
 import org.sonatype.aether.collection.DependencySelector;
 import org.sonatype.aether.graph.Dependency;
@@ -30,27 +32,26 @@ import org.sonatype.aether.graph.Dependency;
  * @see {@link Dependency#getScope()}
  * @author Gabriel Belingueres
  */
-public class Maven30DirectScopeDependencySelector
+class Maven30DirectScopeDependencySelector
     implements DependencySelector
 {
 
     private final String scope;
 
     private final int depth;
+    
+    private final int hashCode;
 
-    public Maven30DirectScopeDependencySelector( String scope )
+    Maven30DirectScopeDependencySelector( String scope )
     {
         this( scope, 0 );
     }
 
-    private Maven30DirectScopeDependencySelector( String scope, int depth )
+    Maven30DirectScopeDependencySelector( String scope, int depth )
     {
-        if ( scope == null )
-        {
-            throw new IllegalArgumentException( "scope is null!" );
-        }
-        this.scope = scope;
+        this.scope = Objects.requireNonNull( scope, "scope is null!" );
         this.depth = depth;
+        this.hashCode = Objects.hash( depth, scope );
     }
 
     /**
@@ -87,11 +88,7 @@ public class Maven30DirectScopeDependencySelector
     @Override
     public int hashCode()
     {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + depth;
-        result = prime * result + ( ( scope == null ) ? 0 : scope.hashCode() );
-        return result;
+        return hashCode;
     }
 
     @Override
@@ -114,14 +111,7 @@ public class Maven30DirectScopeDependencySelector
         {
             return false;
         }
-        if ( scope == null )
-        {
-            if ( other.scope != null )
-            {
-                return false;
-            }
-        }
-        else if ( !scope.equals( other.scope ) )
+        if ( !Objects.equals( scope, other.scope ) )
         {
             return false;
         }
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeDeriver.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeDeriver.java
index 70dc35d..aefaa5e 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeDeriver.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeDeriver.java
@@ -29,7 +29,7 @@ import org.sonatype.aether.util.artifact.JavaScopes;
  * 
  * @author Gabriel Belingueres
  */
-public class Maven30JavaScopeDeriver
+class Maven30JavaScopeDeriver
     extends ScopeDeriver
 {
 
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeSelector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeSelector.java
index f569d40..7e2ecea 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeSelector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30JavaScopeSelector.java
@@ -34,7 +34,7 @@ import org.sonatype.aether.util.artifact.JavaScopes;
  * 
  * @author Gabriel Belingueres
  */
-public class Maven30JavaScopeSelector
+class Maven30JavaScopeSelector
     extends ScopeSelector
 {
 
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NearestVersionSelector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NearestVersionSelector.java
index c913c54..3fc3400 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NearestVersionSelector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NearestVersionSelector.java
@@ -40,7 +40,7 @@ import org.sonatype.aether.version.VersionConstraint;
  * 
  * @author Gabriel Belingueres
  */
-public class Maven30NearestVersionSelector
+class Maven30NearestVersionSelector
     extends VersionSelector
 {
 
@@ -56,7 +56,6 @@ public class Maven30NearestVersionSelector
 
             boolean backtrack = false;
             boolean hardConstraint = !constraint.getRanges().isEmpty();
-//        boolean hardConstraint = constraint.getRange() != null;
 
             if ( hardConstraint )
             {
@@ -151,8 +150,6 @@ public class Maven30NearestVersionSelector
         Maven30PathRecordingDependencyVisitor visitor = new Maven30PathRecordingDependencyVisitor( filter );
         context.getRoot().accept( visitor );
         return new UnsolvableVersionConflictException( visitor.getPaths(), null );
-//    return new UnsolvableVersionConflictException( visitor.getPaths(), context.conflictId );
-//    return new UnsolvableVersionConflictException( visitor.getPaths() );
     }
 
     static final class ConflictGroup
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NodeData.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NodeData.java
index d22f232..87d21e2 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NodeData.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30NodeData.java
@@ -31,11 +31,10 @@ import org.sonatype.aether.graph.DependencyNode;
  * 
  * @author Gabriel Belingueres
  */
-public class Maven30NodeData
+class Maven30NodeData
 {
 
-    private IdentityHashMap<DependencyNode, Map<Object, Object>> nodeMap =
-        new IdentityHashMap<DependencyNode, Map<Object, Object>>();
+    private IdentityHashMap<DependencyNode, Map<Object, Object>> nodeMap = new IdentityHashMap<>();
 
     public void putData( DependencyNode node, Object key, Object value )
     {
@@ -81,7 +80,7 @@ public class Maven30NodeData
         {
             if ( data.isEmpty() )
             {
-                data = new HashMap<Object, Object>();
+                data = new HashMap<>();
             }
             data.put( key, value );
         }
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30PathRecordingDependencyVisitor.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30PathRecordingDependencyVisitor.java
index 59f98ec..3d5e9fe 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30PathRecordingDependencyVisitor.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30PathRecordingDependencyVisitor.java
@@ -32,7 +32,7 @@ import org.sonatype.aether.graph.DependencyVisitor;
  * 
  * @author Gabriel Belingueres
  */
-public class Maven30PathRecordingDependencyVisitor
+class Maven30PathRecordingDependencyVisitor
     implements DependencyVisitor
 {
 
@@ -50,7 +50,7 @@ public class Maven30PathRecordingDependencyVisitor
      * 
      * @param filter The filter used to select terminal nodes of paths to record, may be {@code null} to match any node.
      */
-    public Maven30PathRecordingDependencyVisitor( DependencyFilter filter )
+    Maven30PathRecordingDependencyVisitor( DependencyFilter filter )
     {
         this( filter, true );
     }
@@ -64,7 +64,7 @@ public class Maven30PathRecordingDependencyVisitor
      *            node. If {@code true}, all recorded paths will have only one matching node (namely the terminal node),
      *            if {@code false} a recorded path can consist of multiple matching nodes.
      */
-    public Maven30PathRecordingDependencyVisitor( DependencyFilter filter, boolean excludeChildrenOfMatches )
+    Maven30PathRecordingDependencyVisitor( DependencyFilter filter, boolean excludeChildrenOfMatches )
     {
         this.filter = filter;
         this.excludeChildrenOfMatches = excludeChildrenOfMatches;
@@ -94,6 +94,7 @@ public class Maven30PathRecordingDependencyVisitor
         return paths;
     }
 
+    @Override
     public boolean visitEnter( DependencyNode node )
     {
         boolean accept = filter == null || filter.accept( node, parents );
@@ -115,6 +116,7 @@ public class Maven30PathRecordingDependencyVisitor
         return !( excludeChildrenOfMatches && accept );
     }
 
+    @Override
     public boolean visitLeave( DependencyNode node )
     {
         parents.pop();
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30SimpleOptionalitySelector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30SimpleOptionalitySelector.java
index 4335628..e2a01c2 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30SimpleOptionalitySelector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30SimpleOptionalitySelector.java
@@ -31,7 +31,7 @@ import org.sonatype.aether.RepositoryException;
  * 
  * @author Gabriel Belingueres
  */
-public class Maven30SimpleOptionalitySelector
+class Maven30SimpleOptionalitySelector
     extends OptionalitySelector
 {
 
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30Stack.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30Stack.java
index e15373c..95a6ead 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30Stack.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven30Stack.java
@@ -29,7 +29,7 @@ import java.util.RandomAccess;
  * @param <E> the type of the elements of the stack.
  * @author Gabriel Belingueres
  */
-public class Maven30Stack<E>
+class Maven30Stack<E>
     extends AbstractList<E>
     implements RandomAccess
 {
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DependencyCollector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DependencyCollector.java
index e557a1e..1f0b0a9 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DependencyCollector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DependencyCollector.java
@@ -25,10 +25,7 @@ import java.util.List;
 import org.apache.maven.RepositoryUtils;
 import org.apache.maven.artifact.handler.ArtifactHandler;
 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
-import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.model.Model;
-import org.apache.maven.project.MavenProject;
-import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.shared.transfer.dependencies.DependableCoordinate;
 import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult;
 import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollector;
@@ -156,6 +153,79 @@ class Maven31DependencyCollector
 
         return collectDependencies( request );
     }
+    
+    @Override
+    public CollectorResult collectDependenciesGraph( org.apache.maven.model.Dependency root )
+        throws DependencyCollectorException
+    {
+        ArtifactTypeRegistry typeRegistry =
+                        (ArtifactTypeRegistry) Invoker.invoke( RepositoryUtils.class, "newArtifactTypeRegistry",
+                                                               ArtifactHandlerManager.class, artifactHandlerManager );
+
+        CollectRequest request = new CollectRequest();
+        request.setRoot( toDependency( root, typeRegistry ) );
+
+        return collectDependenciesGraph( request );
+    }
+    
+    @Override
+    public CollectorResult collectDependenciesGraph( DependableCoordinate root )
+        throws DependencyCollectorException
+    {
+        ArtifactHandler artifactHandler = artifactHandlerManager.getArtifactHandler( root.getType() );
+        
+        String extension = artifactHandler != null ? artifactHandler.getExtension() : null;
+        
+        Artifact aetherArtifact = new DefaultArtifact( root.getGroupId(), root.getArtifactId(), root.getClassifier(),
+                                                       extension, root.getVersion() );
+        
+        CollectRequest request = new CollectRequest();
+        request.setRoot( new Dependency( aetherArtifact, null ) );
+
+        return collectDependenciesGraph( request );
+    }
+    
+    @Override
+    public CollectorResult collectDependenciesGraph( Model root )
+        throws DependencyCollectorException
+    {
+        // Are there examples where packaging and type are NOT in sync
+        ArtifactHandler artifactHandler = artifactHandlerManager.getArtifactHandler( root.getPackaging() );
+        
+        String extension = artifactHandler != null ? artifactHandler.getExtension() : null;
+        
+        Artifact aetherArtifact =
+            new DefaultArtifact( root.getGroupId(), root.getArtifactId(), extension, root.getVersion() );
+        
+        CollectRequest request = new CollectRequest();
+        request.setRoot( new Dependency( aetherArtifact, null ) );
+
+        ArtifactTypeRegistry typeRegistry =
+                        (ArtifactTypeRegistry) Invoker.invoke( RepositoryUtils.class, "newArtifactTypeRegistry",
+                                                               ArtifactHandlerManager.class, artifactHandlerManager );
+
+        List<Dependency> aetherDependencies = new ArrayList<Dependency>( root.getDependencies().size() );
+        for ( org.apache.maven.model.Dependency mavenDependency : root.getDependencies() )
+        {
+            aetherDependencies.add( toDependency( mavenDependency, typeRegistry ) );
+        }
+        request.setDependencies( aetherDependencies );
+
+        if ( root.getDependencyManagement() != null )
+        {
+            List<Dependency> aetherManagerDependencies =
+                new ArrayList<Dependency>( root.getDependencyManagement().getDependencies().size() );
+            
+            for ( org.apache.maven.model.Dependency mavenDependency : root.getDependencyManagement().getDependencies() )
+            {
+                aetherManagerDependencies.add( toDependency( mavenDependency, typeRegistry ) );
+            }
+            
+            request.setManagedDependencies( aetherManagerDependencies );
+        }
+        
+        return collectDependenciesGraph( request );
+    }
 
     private CollectorResult collectDependencies( CollectRequest request )
         throws DependencyCollectorException
@@ -172,23 +242,12 @@ class Maven31DependencyCollector
         }
     }
 
-    @Override
-    public CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+    private CollectorResult collectDependenciesGraph( CollectRequest request )
         throws DependencyCollectorException
     {
-        DefaultRepositorySystemSession session = null;
+        DefaultRepositorySystemSession session = new DefaultRepositorySystemSession( this.session );
         try
         {
-            MavenProject project = buildingRequest.getProject();
-
-            org.apache.maven.artifact.Artifact projectArtifact = project.getArtifact();
-            List<ArtifactRepository> remoteArtifactRepositories = project.getRemoteArtifactRepositories();
-
-            DefaultRepositorySystemSession repositorySession =
-                (DefaultRepositorySystemSession) Invoker.invoke( buildingRequest, "getRepositorySession" );
-
-            session = new DefaultRepositorySystemSession( repositorySession );
-
             DependencyGraphTransformer transformer =
                 new ConflictResolver( new NearestVersionSelector(), new JavaScopeSelector(),
                                       new SimpleOptionalitySelector(), new JavaScopeDeriver() );
@@ -202,36 +261,11 @@ class Maven31DependencyCollector
             session.setConfigProperty( ConflictResolver.CONFIG_PROP_VERBOSE, true );
             session.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, true );
 
-            Artifact aetherArtifact =
-                (Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
-                                           org.apache.maven.artifact.Artifact.class, projectArtifact );
-
-            @SuppressWarnings( "unchecked" )
-            List<org.eclipse.aether.repository.RemoteRepository> aetherRepos =
-                (List<org.eclipse.aether.repository.RemoteRepository>) Invoker.invoke( RepositoryUtils.class, "toRepos",
-                                                                                       List.class,
-                                                                                       remoteArtifactRepositories );
-
-            CollectRequest collectRequest = new CollectRequest();
-            collectRequest.setRoot( new org.eclipse.aether.graph.Dependency( aetherArtifact, "" ) );
-            collectRequest.setRepositories( aetherRepos );
-
-            org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
-            collectDependencyList( collectRequest, project, stereotypes );
-            collectManagedDependencyList( collectRequest, project, stereotypes );
+            request.setRepositories( aetherRepositories );
 
-            CollectResult collectResult = repositorySystem.collectDependencies( session, collectRequest );
+            CollectResult collectResult = repositorySystem.collectDependencies( session, request );
 
             return new Maven31CollectorResult( collectResult );
-
-//            org.eclipse.aether.graph.DependencyNode rootNode = collectResult.getRoot();
-
-//            if ( getLogger().isDebugEnabled() )
-//            {
-//                logTree( rootNode );
-//            }
-
-//            return buildDependencyNode( null, rootNode, projectArtifact, filter );
         }
         catch ( DependencyCollectionException e )
         {
@@ -246,31 +280,6 @@ class Maven31DependencyCollector
         }
     }
 
-    private void collectManagedDependencyList( CollectRequest collectRequest, MavenProject project,
-                                               ArtifactTypeRegistry typeRegistry )
-        throws DependencyCollectorException
-    {
-        if ( project.getDependencyManagement() != null )
-        {
-            for ( org.apache.maven.model.Dependency dependency : project.getDependencyManagement().getDependencies() )
-            {
-                Dependency aetherDep = toDependency( dependency, typeRegistry );
-                collectRequest.addManagedDependency( aetherDep );
-            }
-        }
-    }
-
-    private void collectDependencyList( CollectRequest collectRequest, MavenProject project,
-                                        org.eclipse.aether.artifact.ArtifactTypeRegistry typeRegistry )
-        throws DependencyCollectorException
-    {
-        for ( org.apache.maven.model.Dependency dependency : project.getDependencies() )
-        {
-            Dependency aetherDep = toDependency( dependency, typeRegistry );
-            collectRequest.addDependency( aetherDep );
-        }
-    }
-
     private static Dependency toDependency( org.apache.maven.model.Dependency root, ArtifactTypeRegistry typeRegistry )
                     throws DependencyCollectorException
     {
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DirectScopeDependencySelector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DirectScopeDependencySelector.java
index e930597..780c530 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DirectScopeDependencySelector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/Maven31DirectScopeDependencySelector.java
@@ -19,6 +19,8 @@ package org.apache.maven.shared.transfer.dependencies.collect.internal;
  * under the License.
  */
 
+import java.util.Objects;
+
 import org.eclipse.aether.collection.DependencyCollectionContext;
 import org.eclipse.aether.collection.DependencySelector;
 import org.eclipse.aether.graph.Dependency;
@@ -30,27 +32,26 @@ import org.eclipse.aether.graph.Dependency;
  * @see {@link Dependency#getScope()}
  * @author Gabriel Belingueres
  */
-public class Maven31DirectScopeDependencySelector
+class Maven31DirectScopeDependencySelector
     implements DependencySelector
 {
 
     private final String scope;
 
     private final int depth;
+    
+    private final int hashCode;
 
-    public Maven31DirectScopeDependencySelector( String scope )
+    Maven31DirectScopeDependencySelector( String scope )
     {
         this( scope, 0 );
     }
 
     private Maven31DirectScopeDependencySelector( String scope, int depth )
     {
-        if ( scope == null )
-        {
-            throw new IllegalArgumentException( "scope is null!" );
-        }
-        this.scope = scope;
+        this.scope = Objects.requireNonNull( scope, "scope is null!" );
         this.depth = depth;
+        this.hashCode = Objects.hash( scope, depth );
     }
 
     /**
@@ -87,11 +88,7 @@ public class Maven31DirectScopeDependencySelector
     @Override
     public int hashCode()
     {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + depth;
-        result = prime * result + ( ( scope == null ) ? 0 : scope.hashCode() );
-        return result;
+        return hashCode;
     }
 
     @Override
@@ -114,14 +111,7 @@ public class Maven31DirectScopeDependencySelector
         {
             return false;
         }
-        if ( scope == null )
-        {
-            if ( other.scope != null )
-            {
-                return false;
-            }
-        }
-        else if ( !scope.equals( other.scope ) )
+        if ( !Objects.equals( scope, other.scope ) )
         {
             return false;
         }
diff --git a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/MavenDependencyCollector.java b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/MavenDependencyCollector.java
index ff8c314..a98c189 100644
--- a/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/MavenDependencyCollector.java
+++ b/src/main/java/org/apache/maven/shared/transfer/dependencies/collect/internal/MavenDependencyCollector.java
@@ -21,7 +21,6 @@ package org.apache.maven.shared.transfer.dependencies.collect.internal;
 
 import org.apache.maven.model.Dependency;
 import org.apache.maven.model.Model;
-import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.shared.transfer.dependencies.DependableCoordinate;
 import org.apache.maven.shared.transfer.dependencies.collect.CollectorResult;
 import org.apache.maven.shared.transfer.dependencies.collect.DependencyCollectorException;
@@ -42,7 +41,13 @@ public interface MavenDependencyCollector
     CollectorResult collectDependencies( Model root )
         throws DependencyCollectorException;
 
-    CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+    CollectorResult collectDependenciesGraph( Dependency root )
+        throws DependencyCollectorException;
+
+    CollectorResult collectDependenciesGraph( DependableCoordinate root )
+        throws DependencyCollectorException;
+
+    CollectorResult collectDependenciesGraph( Model root )
         throws DependencyCollectorException;
 
 }