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.
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 @@
 import java.util.List;

 

 import org.apache.maven.artifact.repository.ArtifactRepository;

+import org.apache.maven.shared.dependency.graph.DependencyNode;

 

 /**

  * 

@@ -34,4 +35,9 @@
      * @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 @@
     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 @@
         }

     }

 

+    @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 @@
      * @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 @@
  */

 

 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 @@
         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 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.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 @@
         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 @@
  */

 

 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 @@
         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 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 @@
         }

     }

 

+    @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 @@
 
 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 @@
     CollectorResult collectDependencies( Model root )
         throws DependencyCollectorException;
 
+    CollectorResult collectDependenciesGraph( ProjectBuildingRequest buildingRequest )
+        throws DependencyCollectorException;
+
 }