http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactProperties.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactProperties.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactProperties.java
new file mode 100644
index 0000000..1108086
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactProperties.java
@@ -0,0 +1,74 @@
+package org.eclipse.aether.artifact;
+
+/*
+ * 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.
+ */
+
+/**
+ * The keys for common properties of artifacts.
+ * 
+ * @see Artifact#getProperties()
+ */
+public final class ArtifactProperties
+{
+
+    /**
+     * A high-level characterization of the artifact, e.g. "maven-plugin" or 
"test-jar".
+     * 
+     * @see ArtifactType#getId()
+     */
+    public static final String TYPE = "type";
+
+    /**
+     * The programming language this artifact is relevant for, e.g. "java" or 
"none".
+     */
+    public static final String LANGUAGE = "language";
+
+    /**
+     * The (expected) path to the artifact on the local filesystem. An 
artifact which has this property set is assumed
+     * to be not present in any regular repository and likewise has no 
artifact descriptor. Artifact resolution will
+     * verify the path and resolve the artifact if the path actually denotes 
an existing file. If the path isn't valid,
+     * resolution will fail and no attempts to search local/remote 
repositories are made.
+     */
+    public static final String LOCAL_PATH = "localPath";
+
+    /**
+     * A boolean flag indicating whether the artifact presents some kind of 
bundle that physically includes its
+     * dependencies, e.g. a fat WAR.
+     */
+    public static final String INCLUDES_DEPENDENCIES = "includesDependencies";
+
+    /**
+     * A boolean flag indicating whether the artifact is meant to be used for 
the compile/runtime/test build path of a
+     * consumer project.
+     */
+    public static final String CONSTITUTES_BUILD_PATH = "constitutesBuildPath";
+
+    /**
+     * The URL to a web page from which the artifact can be manually 
downloaded. This URL is not contacted by the
+     * repository system but serves as a pointer for the end user to assist in 
getting artifacts that are not published
+     * in a proper repository.
+     */
+    public static final String DOWNLOAD_URL = "downloadUrl";
+
+    private ArtifactProperties()
+    {
+        // hide constructor
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactType.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactType.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactType.java
new file mode 100644
index 0000000..5f87217
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactType.java
@@ -0,0 +1,67 @@
+package org.eclipse.aether.artifact;
+
+/*
+ * 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.Map;
+
+/**
+ * An artifact type describing artifact characteristics/properties that are 
common for certain artifacts. Artifact types
+ * are a means to simplify the description of an artifact by referring to an 
artifact type instead of specifying the
+ * various properties individually.
+ * 
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ * @see ArtifactTypeRegistry
+ * @see DefaultArtifact#DefaultArtifact(String, String, String, String, 
String, ArtifactType)
+ */
+public interface ArtifactType
+{
+
+    /**
+     * Gets the identifier of this type, e.g. "maven-plugin" or "test-jar".
+     * 
+     * @return The identifier of this type, never {@code null}.
+     * @see ArtifactProperties#TYPE
+     */
+    String getId();
+
+    /**
+     * Gets the file extension to use for artifacts of this type (unless 
explicitly overridden by the artifact).
+     * 
+     * @return The usual file extension, never {@code null}.
+     */
+    String getExtension();
+
+    /**
+     * Gets the classifier to use for artifacts of this type (unless 
explicitly overridden by the artifact).
+     * 
+     * @return The usual classifier or an empty string if none, never {@code 
null}.
+     */
+    String getClassifier();
+
+    /**
+     * Gets the properties to use for artifacts of this type (unless 
explicitly overridden by the artifact).
+     * 
+     * @return The (read-only) properties, never {@code null}.
+     * @see ArtifactProperties
+     */
+    Map<String, String> getProperties();
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactTypeRegistry.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactTypeRegistry.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactTypeRegistry.java
new file mode 100644
index 0000000..f379173
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactTypeRegistry.java
@@ -0,0 +1,38 @@
+package org.eclipse.aether.artifact;
+
+/*
+ * 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.
+ */
+
+/**
+ * A registry of known artifact types.
+ * 
+ * @see org.eclipse.aether.RepositorySystemSession#getArtifactTypeRegistry()
+ */
+public interface ArtifactTypeRegistry
+{
+
+    /**
+     * Gets the artifact type with the specified identifier.
+     * 
+     * @param typeId The identifier of the type, must not be {@code null}.
+     * @return The artifact type or {@code null} if no type with the requested 
identifier exists.
+     */
+    ArtifactType get( String typeId );
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java
new file mode 100644
index 0000000..786af74
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java
@@ -0,0 +1,285 @@
+package org.eclipse.aether.artifact;
+
+/*
+ * 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.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A simple artifact. <em>Note:</em> Instances of this class are immutable and 
the exposed mutators return new objects
+ * rather than changing the current instance.
+ */
+public final class DefaultArtifact
+    extends AbstractArtifact
+{
+
+    private final String groupId;
+
+    private final String artifactId;
+
+    private final String version;
+
+    private final String classifier;
+
+    private final String extension;
+
+    private final File file;
+
+    private final Map<String, String> properties;
+
+    /**
+     * Creates a new artifact with the specified coordinates. If not specified 
in the artifact coordinates, the
+     * artifact's extension defaults to {@code jar} and classifier to an empty 
string.
+     * 
+     * @param coords The artifact coordinates in the format
+     *            {@code 
<groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be 
{@code null}.
+     */
+    public DefaultArtifact( String coords )
+    {
+        this( coords, Collections.<String, String>emptyMap() );
+    }
+
+    /**
+     * Creates a new artifact with the specified coordinates and properties. 
If not specified in the artifact
+     * coordinates, the artifact's extension defaults to {@code jar} and 
classifier to an empty string.
+     * 
+     * @param coords The artifact coordinates in the format
+     *            {@code 
<groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be 
{@code null}.
+     * @param properties The artifact properties, may be {@code null}.
+     */
+    public DefaultArtifact( String coords, Map<String, String> properties )
+    {
+        Pattern p = Pattern.compile( "([^: ]+):([^: ]+)(:([^: ]*)(:([^: 
]+))?)?:([^: ]+)" );
+        Matcher m = p.matcher( coords );
+        if ( !m.matches() )
+        {
+            throw new IllegalArgumentException( "Bad artifact coordinates " + 
coords
+                + ", expected format is 
<groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>" );
+        }
+        groupId = m.group( 1 );
+        artifactId = m.group( 2 );
+        extension = get( m.group( 4 ), "jar" );
+        classifier = get( m.group( 6 ), "" );
+        version = m.group( 7 );
+        file = null;
+        this.properties = copyProperties( properties );
+    }
+
+    private static String get( String value, String defaultValue )
+    {
+        return ( value == null || value.length() <= 0 ) ? defaultValue : value;
+    }
+
+    /**
+     * Creates a new artifact with the specified coordinates and no 
classifier. Passing {@code null} for any of the
+     * coordinates is equivalent to specifying an empty string.
+     * 
+     * @param groupId The group identifier of the artifact, may be {@code 
null}.
+     * @param artifactId The artifact identifier of the artifact, may be 
{@code null}.
+     * @param extension The file extension of the artifact, may be {@code 
null}.
+     * @param version The version of the artifact, may be {@code null}.
+     */
+    public DefaultArtifact( String groupId, String artifactId, String 
extension, String version )
+    {
+        this( groupId, artifactId, "", extension, version );
+    }
+
+    /**
+     * Creates a new artifact with the specified coordinates. Passing {@code 
null} for any of the coordinates is
+     * equivalent to specifying an empty string.
+     * 
+     * @param groupId The group identifier of the artifact, may be {@code 
null}.
+     * @param artifactId The artifact identifier of the artifact, may be 
{@code null}.
+     * @param classifier The classifier of the artifact, may be {@code null}.
+     * @param extension The file extension of the artifact, may be {@code 
null}.
+     * @param version The version of the artifact, may be {@code null}.
+     */
+    public DefaultArtifact( String groupId, String artifactId, String 
classifier, String extension, String version )
+    {
+        this( groupId, artifactId, classifier, extension, version, null, 
(File) null );
+    }
+
+    /**
+     * Creates a new artifact with the specified coordinates. Passing {@code 
null} for any of the coordinates is
+     * equivalent to specifying an empty string. The optional artifact type 
provided to this constructor will be used to
+     * determine the artifact's classifier and file extension if the 
corresponding arguments for this constructor are
+     * {@code null}.
+     * 
+     * @param groupId The group identifier of the artifact, may be {@code 
null}.
+     * @param artifactId The artifact identifier of the artifact, may be 
{@code null}.
+     * @param classifier The classifier of the artifact, may be {@code null}.
+     * @param extension The file extension of the artifact, may be {@code 
null}.
+     * @param version The version of the artifact, may be {@code null}.
+     * @param type The artifact type from which to query classifier, file 
extension and properties, may be {@code null}.
+     */
+    public DefaultArtifact( String groupId, String artifactId, String 
classifier, String extension, String version,
+                            ArtifactType type )
+    {
+        this( groupId, artifactId, classifier, extension, version, null, type 
);
+    }
+
+    /**
+     * Creates a new artifact with the specified coordinates and properties. 
Passing {@code null} for any of the
+     * coordinates is equivalent to specifying an empty string. The optional 
artifact type provided to this constructor
+     * will be used to determine the artifact's classifier and file extension 
if the corresponding arguments for this
+     * constructor are {@code null}. If the artifact type specifies 
properties, those will get merged with the
+     * properties passed directly into the constructor, with the latter 
properties taking precedence.
+     * 
+     * @param groupId The group identifier of the artifact, may be {@code 
null}.
+     * @param artifactId The artifact identifier of the artifact, may be 
{@code null}.
+     * @param classifier The classifier of the artifact, may be {@code null}.
+     * @param extension The file extension of the artifact, may be {@code 
null}.
+     * @param version The version of the artifact, may be {@code null}.
+     * @param properties The properties of the artifact, may be {@code null} 
if none.
+     * @param type The artifact type from which to query classifier, file 
extension and properties, may be {@code null}.
+     */
+    public DefaultArtifact( String groupId, String artifactId, String 
classifier, String extension, String version,
+                            Map<String, String> properties, ArtifactType type )
+    {
+        this.groupId = emptify( groupId );
+        this.artifactId = emptify( artifactId );
+        if ( classifier != null || type == null )
+        {
+            this.classifier = emptify( classifier );
+        }
+        else
+        {
+            this.classifier = emptify( type.getClassifier() );
+        }
+        if ( extension != null || type == null )
+        {
+            this.extension = emptify( extension );
+        }
+        else
+        {
+            this.extension = emptify( type.getExtension() );
+        }
+        this.version = emptify( version );
+        this.file = null;
+        this.properties = merge( properties, ( type != null ) ? 
type.getProperties() : null );
+    }
+
+    private static Map<String, String> merge( Map<String, String> dominant, 
Map<String, String> recessive )
+    {
+        Map<String, String> properties;
+
+        if ( ( dominant == null || dominant.isEmpty() ) && ( recessive == null 
|| recessive.isEmpty() ) )
+        {
+            properties = Collections.emptyMap();
+        }
+        else
+        {
+            properties = new HashMap<String, String>();
+            if ( recessive != null )
+            {
+                properties.putAll( recessive );
+            }
+            if ( dominant != null )
+            {
+                properties.putAll( dominant );
+            }
+            properties = Collections.unmodifiableMap( properties );
+        }
+
+        return properties;
+    }
+
+    /**
+     * Creates a new artifact with the specified coordinates, properties and 
file. Passing {@code null} for any of the
+     * coordinates is equivalent to specifying an empty string.
+     * 
+     * @param groupId The group identifier of the artifact, may be {@code 
null}.
+     * @param artifactId The artifact identifier of the artifact, may be 
{@code null}.
+     * @param classifier The classifier of the artifact, may be {@code null}.
+     * @param extension The file extension of the artifact, may be {@code 
null}.
+     * @param version The version of the artifact, may be {@code null}.
+     * @param properties The properties of the artifact, may be {@code null} 
if none.
+     * @param file The resolved file of the artifact, may be {@code null}.
+     */
+    public DefaultArtifact( String groupId, String artifactId, String 
classifier, String extension, String version,
+                            Map<String, String> properties, File file )
+    {
+        this.groupId = emptify( groupId );
+        this.artifactId = emptify( artifactId );
+        this.classifier = emptify( classifier );
+        this.extension = emptify( extension );
+        this.version = emptify( version );
+        this.file = file;
+        this.properties = copyProperties( properties );
+    }
+
+    DefaultArtifact( String groupId, String artifactId, String classifier, 
String extension, String version, File file,
+                     Map<String, String> properties )
+    {
+        // NOTE: This constructor assumes immutability of the provided 
properties, for internal use only
+        this.groupId = emptify( groupId );
+        this.artifactId = emptify( artifactId );
+        this.classifier = emptify( classifier );
+        this.extension = emptify( extension );
+        this.version = emptify( version );
+        this.file = file;
+        this.properties = properties;
+    }
+
+    private static String emptify( String str )
+    {
+        return ( str == null ) ? "" : str;
+    }
+
+    public String getGroupId()
+    {
+        return groupId;
+    }
+
+    public String getArtifactId()
+    {
+        return artifactId;
+    }
+
+    public String getVersion()
+    {
+        return version;
+    }
+
+    public String getClassifier()
+    {
+        return classifier;
+    }
+
+    public String getExtension()
+    {
+        return extension;
+    }
+
+    public File getFile()
+    {
+        return file;
+    }
+
+    public Map<String, String> getProperties()
+    {
+        return properties;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifactType.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifactType.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifactType.java
new file mode 100644
index 0000000..c0d2ad8
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifactType.java
@@ -0,0 +1,146 @@
+package org.eclipse.aether.artifact;
+
+/*
+ * 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.Map;
+
+/**
+ * A simple artifact type.
+ */
+public final class DefaultArtifactType
+    implements ArtifactType
+{
+
+    private final String id;
+
+    private final String extension;
+
+    private final String classifier;
+
+    private final Map<String, String> properties;
+
+    /**
+     * Creates a new artifact type with the specified identifier. This 
constructor assumes the usual file extension
+     * equals the given type id and that the usual classifier is empty. 
Additionally, the properties
+     * {@link ArtifactProperties#LANGUAGE}, {@link 
ArtifactProperties#CONSTITUTES_BUILD_PATH} and
+     * {@link ArtifactProperties#INCLUDES_DEPENDENCIES} will be set to {@code 
"none"}, {@code true} and {@code false},
+     * respectively.
+     * 
+     * @param id The identifier of the type which will also be used as the 
value for the {@link ArtifactProperties#TYPE}
+     *            property, must not be {@code null} or empty.
+     */
+    public DefaultArtifactType( String id )
+    {
+        this( id, id, "", "none", false, false );
+    }
+
+    /**
+     * Creates a new artifact type with the specified properties. 
Additionally, the properties
+     * {@link ArtifactProperties#CONSTITUTES_BUILD_PATH} and {@link 
ArtifactProperties#INCLUDES_DEPENDENCIES} will be
+     * set to {@code true} and {@code false}, respectively.
+     * 
+     * @param id The identifier of the type which will also be used as the 
value for the {@link ArtifactProperties#TYPE}
+     *            property, must not be {@code null} or empty.
+     * @param extension The usual file extension for artifacts of this type, 
may be {@code null}.
+     * @param classifier The usual classifier for artifacts of this type, may 
be {@code null}.
+     * @param language The value for the {@link ArtifactProperties#LANGUAGE} 
property, may be {@code null}.
+     */
+    public DefaultArtifactType( String id, String extension, String 
classifier, String language )
+    {
+        this( id, extension, classifier, language, true, false );
+    }
+
+    /**
+     * Creates a new artifact type with the specified properties.
+     * 
+     * @param id The identifier of the type which will also be used as the 
value for the {@link ArtifactProperties#TYPE}
+     *            property, must not be {@code null} or empty.
+     * @param extension The usual file extension for artifacts of this type, 
may be {@code null}.
+     * @param classifier The usual classifier for artifacts of this type, may 
be {@code null}.
+     * @param language The value for the {@link ArtifactProperties#LANGUAGE} 
property, may be {@code null}.
+     * @param constitutesBuildPath The value for the {@link 
ArtifactProperties#CONSTITUTES_BUILD_PATH} property.
+     * @param includesDependencies The value for the {@link 
ArtifactProperties#INCLUDES_DEPENDENCIES} property.
+     */
+    public DefaultArtifactType( String id, String extension, String 
classifier, String language,
+                                boolean constitutesBuildPath, boolean 
includesDependencies )
+    {
+        if ( id == null || id.length() < 0 )
+        {
+            throw new IllegalArgumentException( "no type id specified" );
+        }
+        this.id = id;
+        this.extension = emptify( extension );
+        this.classifier = emptify( classifier );
+        Map<String, String> props = new HashMap<String, String>();
+        props.put( ArtifactProperties.TYPE, id );
+        props.put( ArtifactProperties.LANGUAGE, ( language != null && 
language.length() > 0 ) ? language : "none" );
+        props.put( ArtifactProperties.INCLUDES_DEPENDENCIES, Boolean.toString( 
includesDependencies ) );
+        props.put( ArtifactProperties.CONSTITUTES_BUILD_PATH, 
Boolean.toString( constitutesBuildPath ) );
+        properties = Collections.unmodifiableMap( props );
+    }
+
+    /**
+     * Creates a new artifact type with the specified properties.
+     * 
+     * @param id The identifier of the type, must not be {@code null} or empty.
+     * @param extension The usual file extension for artifacts of this type, 
may be {@code null}.
+     * @param classifier The usual classifier for artifacts of this type, may 
be {@code null}.
+     * @param properties The properties for artifacts of this type, may be 
{@code null}.
+     */
+    public DefaultArtifactType( String id, String extension, String 
classifier, Map<String, String> properties )
+    {
+        if ( id == null || id.length() < 0 )
+        {
+            throw new IllegalArgumentException( "no type id specified" );
+        }
+        this.id = id;
+        this.extension = emptify( extension );
+        this.classifier = emptify( classifier );
+        this.properties = AbstractArtifact.copyProperties( properties );
+    }
+
+    private static String emptify( String str )
+    {
+        return ( str == null ) ? "" : str;
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    public String getExtension()
+    {
+        return extension;
+    }
+
+    public String getClassifier()
+    {
+        return classifier;
+    }
+
+    public Map<String, String> getProperties()
+    {
+        return properties;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/package-info.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/package-info.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/package-info.java
new file mode 100644
index 0000000..9a4cc79
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/package-info.java
@@ -0,0 +1,24 @@
+// CHECKSTYLE_OFF: RegexpHeader
+/*
+ * 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.
+ */
+/**
+ * The definition of an artifact, that is the primary entity managed by the 
repository system.
+ */
+package org.eclipse.aether.artifact;
+

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectRequest.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectRequest.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectRequest.java
new file mode 100644
index 0000000..d9c2527
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectRequest.java
@@ -0,0 +1,356 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.Collections;
+import java.util.List;
+
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.RequestTrace;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.repository.RemoteRepository;
+
+/**
+ * A request to collect the transitive dependencies and to build a dependency 
graph from them. There are three ways to
+ * create a dependency graph. First, only the root dependency can be given. 
Second, a root dependency and direct
+ * dependencies can be specified in which case the specified direct 
dependencies are merged with the direct dependencies
+ * retrieved from the artifact descriptor of the root dependency. And last, 
only direct dependencies can be specified in
+ * which case the root node of the resulting graph has no associated 
dependency.
+ * 
+ * @see RepositorySystem#collectDependencies(RepositorySystemSession, 
CollectRequest)
+ */
+public final class CollectRequest
+{
+
+    private Artifact rootArtifact;
+
+    private Dependency root;
+
+    private List<Dependency> dependencies = Collections.emptyList();
+
+    private List<Dependency> managedDependencies = Collections.emptyList();
+
+    private List<RemoteRepository> repositories = Collections.emptyList();
+
+    private String context = "";
+
+    private RequestTrace trace;
+
+    /**
+     * Creates an uninitialized request.
+     */
+    public CollectRequest()
+    {
+        // enables default constructor
+    }
+
+    /**
+     * Creates a request with the specified properties.
+     * 
+     * @param root The root dependency whose transitive dependencies should be 
collected, may be {@code null}.
+     * @param repositories The repositories to use for the collection, may be 
{@code null}.
+     */
+    public CollectRequest( Dependency root, List<RemoteRepository> 
repositories )
+    {
+        setRoot( root );
+        setRepositories( repositories );
+    }
+
+    /**
+     * Creates a new request with the specified properties.
+     * 
+     * @param root The root dependency whose transitive dependencies should be 
collected, may be {@code null}.
+     * @param dependencies The direct dependencies to merge with the direct 
dependencies from the root dependency's
+     *            artifact descriptor.
+     * @param repositories The repositories to use for the collection, may be 
{@code null}.
+     */
+    public CollectRequest( Dependency root, List<Dependency> dependencies, 
List<RemoteRepository> repositories )
+    {
+        setRoot( root );
+        setDependencies( dependencies );
+        setRepositories( repositories );
+    }
+
+    /**
+     * Creates a new request with the specified properties.
+     * 
+     * @param dependencies The direct dependencies of some imaginary root, may 
be {@code null}.
+     * @param managedDependencies The dependency management information to 
apply to the transitive dependencies, may be
+     *            {@code null}.
+     * @param repositories The repositories to use for the collection, may be 
{@code null}.
+     */
+    public CollectRequest( List<Dependency> dependencies, List<Dependency> 
managedDependencies,
+                           List<RemoteRepository> repositories )
+    {
+        setDependencies( dependencies );
+        setManagedDependencies( managedDependencies );
+        setRepositories( repositories );
+    }
+
+    /**
+     * Gets the root artifact for the dependency graph.
+     * 
+     * @return The root artifact for the dependency graph or {@code null} if 
none.
+     */
+    public Artifact getRootArtifact()
+    {
+        return rootArtifact;
+    }
+
+    /**
+     * Sets the root artifact for the dependency graph. This must not be 
confused with {@link #setRoot(Dependency)}: The
+     * root <em>dependency</em>, like any other specified dependency, will be 
subject to dependency
+     * collection/resolution, i.e. should have an artifact descriptor and a 
corresponding artifact file. The root
+     * <em>artifact</em> on the other hand is only used as a label for the 
root node of the graph in case no root
+     * dependency was specified. As such, the configured root artifact is 
ignored if {@link #getRoot()} does not return
+     * {@code null}.
+     * 
+     * @param rootArtifact The root artifact for the dependency graph, may be 
{@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public CollectRequest setRootArtifact( Artifact rootArtifact )
+    {
+        this.rootArtifact = rootArtifact;
+        return this;
+    }
+
+    /**
+     * Gets the root dependency of the graph.
+     * 
+     * @return The root dependency of the graph or {@code null} if none.
+     */
+    public Dependency getRoot()
+    {
+        return root;
+    }
+
+    /**
+     * Sets the root dependency of the graph.
+     * 
+     * @param root The root dependency of the graph, may be {@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public CollectRequest setRoot( Dependency root )
+    {
+        this.root = root;
+        return this;
+    }
+
+    /**
+     * Gets the direct dependencies.
+     * 
+     * @return The direct dependencies, never {@code null}.
+     */
+    public List<Dependency> getDependencies()
+    {
+        return dependencies;
+    }
+
+    /**
+     * Sets the direct dependencies. If both a root dependency and direct 
dependencies are given in the request, the
+     * direct dependencies from the request will be merged with the direct 
dependencies from the root dependency's
+     * artifact descriptor, giving higher priority to the dependencies from 
the request.
+     * 
+     * @param dependencies The direct dependencies, may be {@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public CollectRequest setDependencies( List<Dependency> dependencies )
+    {
+        if ( dependencies == null )
+        {
+            this.dependencies = Collections.emptyList();
+        }
+        else
+        {
+            this.dependencies = dependencies;
+        }
+        return this;
+    }
+
+    /**
+     * Adds the specified direct dependency.
+     * 
+     * @param dependency The dependency to add, may be {@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public CollectRequest addDependency( Dependency dependency )
+    {
+        if ( dependency != null )
+        {
+            if ( this.dependencies.isEmpty() )
+            {
+                this.dependencies = new ArrayList<Dependency>();
+            }
+            this.dependencies.add( dependency );
+        }
+        return this;
+    }
+
+    /**
+     * Gets the dependency management to apply to transitive dependencies.
+     * 
+     * @return The dependency management to apply to transitive dependencies, 
never {@code null}.
+     */
+    public List<Dependency> getManagedDependencies()
+    {
+        return managedDependencies;
+    }
+
+    /**
+     * Sets the dependency management to apply to transitive dependencies. To 
clarify, this management does not apply to
+     * the direct dependencies of the root node.
+     * 
+     * @param managedDependencies The dependency management, may be {@code 
null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public CollectRequest setManagedDependencies( List<Dependency> 
managedDependencies )
+    {
+        if ( managedDependencies == null )
+        {
+            this.managedDependencies = Collections.emptyList();
+        }
+        else
+        {
+            this.managedDependencies = managedDependencies;
+        }
+        return this;
+    }
+
+    /**
+     * Adds the specified managed dependency.
+     * 
+     * @param managedDependency The managed dependency to add, may be {@code 
null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public CollectRequest addManagedDependency( Dependency managedDependency )
+    {
+        if ( managedDependency != null )
+        {
+            if ( this.managedDependencies.isEmpty() )
+            {
+                this.managedDependencies = new ArrayList<Dependency>();
+            }
+            this.managedDependencies.add( managedDependency );
+        }
+        return this;
+    }
+
+    /**
+     * Gets the repositories to use for the collection.
+     * 
+     * @return The repositories to use for the collection, never {@code null}.
+     */
+    public List<RemoteRepository> getRepositories()
+    {
+        return repositories;
+    }
+
+    /**
+     * Sets the repositories to use for the collection.
+     * 
+     * @param repositories The repositories to use for the collection, may be 
{@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public CollectRequest setRepositories( List<RemoteRepository> repositories 
)
+    {
+        if ( repositories == null )
+        {
+            this.repositories = Collections.emptyList();
+        }
+        else
+        {
+            this.repositories = repositories;
+        }
+        return this;
+    }
+
+    /**
+     * Adds the specified repository for collection.
+     * 
+     * @param repository The repository to collect dependency information 
from, may be {@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public CollectRequest addRepository( RemoteRepository repository )
+    {
+        if ( repository != null )
+        {
+            if ( this.repositories.isEmpty() )
+            {
+                this.repositories = new ArrayList<RemoteRepository>();
+            }
+            this.repositories.add( repository );
+        }
+        return this;
+    }
+
+    /**
+     * Gets the context in which this request is made.
+     * 
+     * @return The context, never {@code null}.
+     */
+    public String getRequestContext()
+    {
+        return context;
+    }
+
+    /**
+     * Sets the context in which this request is made.
+     * 
+     * @param context The context, may be {@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public CollectRequest setRequestContext( String context )
+    {
+        this.context = ( context != null ) ? context : "";
+        return this;
+    }
+
+    /**
+     * Gets the trace information that describes the higher level 
request/operation in which this request is issued.
+     * 
+     * @return The trace information about the higher level operation or 
{@code null} if none.
+     */
+    public RequestTrace getTrace()
+    {
+        return trace;
+    }
+
+    /**
+     * Sets the trace information that describes the higher level 
request/operation in which this request is issued.
+     * 
+     * @param trace The trace information about the higher level operation, 
may be {@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public CollectRequest setTrace( RequestTrace trace )
+    {
+        this.trace = trace;
+        return this;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getRoot() + " -> " + getDependencies() + " < " + 
getRepositories();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectResult.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectResult.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectResult.java
new file mode 100644
index 0000000..dd9f02e
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectResult.java
@@ -0,0 +1,159 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.Collections;
+import java.util.List;
+
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.graph.DependencyCycle;
+import org.eclipse.aether.graph.DependencyNode;
+
+/**
+ * The result of a dependency collection request.
+ * 
+ * @see RepositorySystem#collectDependencies(RepositorySystemSession, 
CollectRequest)
+ */
+public final class CollectResult
+{
+
+    private final CollectRequest request;
+
+    private List<Exception> exceptions;
+
+    private List<DependencyCycle> cycles;
+
+    private DependencyNode root;
+
+    /**
+     * Creates a new result for the specified request.
+     * 
+     * @param request The resolution request, must not be {@code null}.
+     */
+    public CollectResult( CollectRequest request )
+    {
+        if ( request == null )
+        {
+            throw new IllegalArgumentException( "dependency collection request 
has not been specified" );
+        }
+        this.request = request;
+        exceptions = Collections.emptyList();
+        cycles = Collections.emptyList();
+    }
+
+    /**
+     * Gets the collection request that was made.
+     * 
+     * @return The collection request, never {@code null}.
+     */
+    public CollectRequest getRequest()
+    {
+        return request;
+    }
+
+    /**
+     * Gets the exceptions that occurred while building the dependency graph.
+     * 
+     * @return The exceptions that occurred, never {@code null}.
+     */
+    public List<Exception> getExceptions()
+    {
+        return exceptions;
+    }
+
+    /**
+     * Records the specified exception while building the dependency graph.
+     * 
+     * @param exception The exception to record, may be {@code null}.
+     * @return This result for chaining, never {@code null}.
+     */
+    public CollectResult addException( Exception exception )
+    {
+        if ( exception != null )
+        {
+            if ( exceptions.isEmpty() )
+            {
+                exceptions = new ArrayList<Exception>();
+            }
+            exceptions.add( exception );
+        }
+        return this;
+    }
+
+    /**
+     * Gets the dependency cycles that were encountered while building the 
dependency graph.
+     * 
+     * @return The dependency cycles in the (raw) graph, never {@code null}.
+     */
+    public List<DependencyCycle> getCycles()
+    {
+        return cycles;
+    }
+
+    /**
+     * Records the specified dependency cycle.
+     * 
+     * @param cycle The dependency cycle to record, may be {@code null}.
+     * @return This result for chaining, never {@code null}.
+     */
+    public CollectResult addCycle( DependencyCycle cycle )
+    {
+        if ( cycle != null )
+        {
+            if ( cycles.isEmpty() )
+            {
+                cycles = new ArrayList<DependencyCycle>();
+            }
+            cycles.add( cycle );
+        }
+        return this;
+    }
+
+    /**
+     * Gets the root node of the dependency graph.
+     * 
+     * @return The root node of the dependency graph or {@code null} if none.
+     */
+    public DependencyNode getRoot()
+    {
+        return root;
+    }
+
+    /**
+     * Sets the root node of the dependency graph.
+     * 
+     * @param root The root node of the dependency graph, may be {@code null}.
+     * @return This result for chaining, never {@code null}.
+     */
+    public CollectResult setRoot( DependencyNode root )
+    {
+        this.root = root;
+        return this;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.valueOf( getRoot() );
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionContext.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionContext.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionContext.java
new file mode 100644
index 0000000..671bd2a
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionContext.java
@@ -0,0 +1,75 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.List;
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.graph.Dependency;
+
+/**
+ * A context used during dependency collection to update the dependency 
manager, selector and traverser.
+ * 
+ * @see DependencyManager#deriveChildManager(DependencyCollectionContext)
+ * @see DependencyTraverser#deriveChildTraverser(DependencyCollectionContext)
+ * @see DependencySelector#deriveChildSelector(DependencyCollectionContext)
+ * @see VersionFilter#deriveChildFilter(DependencyCollectionContext)
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface DependencyCollectionContext
+{
+
+    /**
+     * Gets the repository system session during which the dependency 
collection happens.
+     * 
+     * @return The repository system session, never {@code null}.
+     */
+    RepositorySystemSession getSession();
+
+    /**
+     * Gets the artifact whose children are to be processed next during 
dependency collection. For all nodes but the
+     * root, this is simply shorthand for {@code 
getDependency().getArtifact()}. In case of the root node however,
+     * {@link #getDependency()} might be {@code null} while the node still has 
an artifact which serves as its label and
+     * is not to be resolved.
+     * 
+     * @return The artifact whose children are going to be processed or {@code 
null} in case of the root node without
+     *         dependency and label.
+     */
+    Artifact getArtifact();
+
+    /**
+     * Gets the dependency whose children are to be processed next during 
dependency collection.
+     * 
+     * @return The dependency whose children are going to be processed or 
{@code null} in case of the root node without
+     *         dependency.
+     */
+    Dependency getDependency();
+
+    /**
+     * Gets the dependency management information that was contributed by the 
artifact descriptor of the current
+     * dependency.
+     * 
+     * @return The dependency management information, never {@code null}.
+     */
+    List<Dependency> getManagedDependencies();
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionException.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionException.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionException.java
new file mode 100644
index 0000000..8a04d79
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionException.java
@@ -0,0 +1,111 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.RepositoryException;
+
+/**
+ * Thrown in case of bad artifact descriptors, version ranges or other issues 
encountered during calculation of the
+ * dependency graph.
+ */
+public class DependencyCollectionException
+    extends RepositoryException
+{
+
+    private final transient CollectResult result;
+
+    /**
+     * Creates a new exception with the specified result.
+     * 
+     * @param result The collection result at the point the exception 
occurred, may be {@code null}.
+     */
+    public DependencyCollectionException( CollectResult result )
+    {
+        super( "Failed to collect dependencies for " + getSource( result ), 
getCause( result ) );
+        this.result = result;
+    }
+
+    /**
+     * Creates a new exception with the specified result and detail message.
+     * 
+     * @param result The collection result at the point the exception 
occurred, may be {@code null}.
+     * @param message The detail message, may be {@code null}.
+     */
+    public DependencyCollectionException( CollectResult result, String message 
)
+    {
+        super( message, getCause( result ) );
+        this.result = result;
+    }
+
+    /**
+     * Creates a new exception with the specified result, detail message and 
cause.
+     * 
+     * @param result The collection result at the point the exception 
occurred, may be {@code null}.
+     * @param message The detail message, may be {@code null}.
+     * @param cause The exception that caused this one, may be {@code null}.
+     */
+    public DependencyCollectionException( CollectResult result, String 
message, Throwable cause )
+    {
+        super( message, cause );
+        this.result = result;
+    }
+
+    /**
+     * Gets the collection result at the point the exception occurred. Despite 
being incomplete, callers might want to
+     * use this result to fail gracefully and continue their operation with 
whatever interim data has been gathered.
+     * 
+     * @return The collection result or {@code null} if unknown.
+     */
+    public CollectResult getResult()
+    {
+        return result;
+    }
+
+    private static String getSource( CollectResult result )
+    {
+        if ( result == null )
+        {
+            return "";
+        }
+
+        CollectRequest request = result.getRequest();
+        if ( request.getRoot() != null )
+        {
+            return request.getRoot().toString();
+        }
+        if ( request.getRootArtifact() != null )
+        {
+            return request.getRootArtifact().toString();
+        }
+
+        return request.getDependencies().toString();
+    }
+
+    private static Throwable getCause( CollectResult result )
+    {
+        Throwable cause = null;
+        if ( result != null && !result.getExceptions().isEmpty() )
+        {
+            cause = result.getExceptions().get( 0 );
+        }
+        return cause;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformationContext.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformationContext.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformationContext.java
new file mode 100644
index 0000000..ba66474
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformationContext.java
@@ -0,0 +1,58 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.RepositorySystemSession;
+
+/**
+ * A context used during dependency collection to exchange information within 
a chain of dependency graph transformers.
+ * 
+ * @see DependencyGraphTransformer
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface DependencyGraphTransformationContext
+{
+
+    /**
+     * Gets the repository system session during which the graph 
transformation happens.
+     * 
+     * @return The repository system session, never {@code null}.
+     */
+    RepositorySystemSession getSession();
+
+    /**
+     * Gets a keyed value from the context.
+     * 
+     * @param key The key used to query the value, must not be {@code null}.
+     * @return The queried value or {@code null} if none.
+     */
+    Object get( Object key );
+
+    /**
+     * Puts a keyed value into the context.
+     * 
+     * @param key The key used to store the value, must not be {@code null}.
+     * @param value The value to store, may be {@code null} to remove the 
mapping.
+     * @return The previous value associated with the key or {@code null} if 
none.
+     */
+    Object put( Object key, Object value );
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformer.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformer.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformer.java
new file mode 100644
index 0000000..c472500
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformer.java
@@ -0,0 +1,51 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.RepositoryException;
+import org.eclipse.aether.graph.DependencyNode;
+
+/**
+ * Transforms a given dependency graph.
+ * <p>
+ * <strong>Note:</strong> Implementations must be stateless.
+ * <p>
+ * <em>Warning:</em> Dependency graphs may generally contain cycles. As such a 
graph transformer that cannot assume for
+ * sure that cycles have already been eliminated must gracefully handle cyclic 
graphs, e.g. guard against infinite
+ * recursion.
+ * 
+ * @see 
org.eclipse.aether.RepositorySystemSession#getDependencyGraphTransformer()
+ */
+public interface DependencyGraphTransformer
+{
+
+    /**
+     * Transforms the dependency graph denoted by the specified root node. The 
transformer may directly change the
+     * provided input graph or create a new graph, the former is recommended 
for performance reasons.
+     * 
+     * @param node 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 result graph of the transformation, never {@code null}.
+     * @throws RepositoryException If the transformation failed.
+     */
+    DependencyNode transformGraph( DependencyNode node, 
DependencyGraphTransformationContext context )
+        throws RepositoryException;
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java
new file mode 100644
index 0000000..054bfe0
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java
@@ -0,0 +1,177 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.Map;
+
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.graph.Exclusion;
+
+/**
+ * The management updates to apply to a dependency.
+ * 
+ * @see DependencyManager#manageDependency(Dependency)
+ */
+public final class DependencyManagement
+{
+
+    private String version;
+
+    private String scope;
+
+    private Boolean optional;
+
+    private Collection<Exclusion> exclusions;
+
+    private Map<String, String> properties;
+
+    /**
+     * Creates an empty management update.
+     */
+    public DependencyManagement()
+    {
+        // enables default constructor
+    }
+
+    /**
+     * Gets the new version to apply to the dependency.
+     * 
+     * @return The new version or {@code null} if the version is not managed 
and the existing dependency version should
+     *         remain unchanged.
+     */
+    public String getVersion()
+    {
+        return version;
+    }
+
+    /**
+     * Sets the new version to apply to the dependency.
+     * 
+     * @param version The new version, may be {@code null} if the version is 
not managed.
+     * @return This management update for chaining, never {@code null}.
+     */
+    public DependencyManagement setVersion( String version )
+    {
+        this.version = version;
+        return this;
+    }
+
+    /**
+     * Gets the new scope to apply to the dependency.
+     * 
+     * @return The new scope or {@code null} if the scope is not managed and 
the existing dependency scope should remain
+     *         unchanged.
+     */
+    public String getScope()
+    {
+        return scope;
+    }
+
+    /**
+     * Sets the new scope to apply to the dependency.
+     * 
+     * @param scope The new scope, may be {@code null} if the scope is not 
managed.
+     * @return This management update for chaining, never {@code null}.
+     */
+    public DependencyManagement setScope( String scope )
+    {
+        this.scope = scope;
+        return this;
+    }
+
+    /**
+     * Gets the new optional flag to apply to the dependency.
+     * 
+     * @return The new optional flag or {@code null} if the flag is not 
managed and the existing optional flag of the
+     *         dependency should remain unchanged.
+     */
+    public Boolean getOptional()
+    {
+        return optional;
+    }
+
+    /**
+     * Sets the new optional flag to apply to the dependency.
+     * 
+     * @param optional The optional flag, may be {@code null} if the flag is 
not managed.
+     * @return This management update for chaining, never {@code null}.
+     */
+    public DependencyManagement setOptional( Boolean optional )
+    {
+        this.optional = optional;
+        return this;
+    }
+
+    /**
+     * Gets the new exclusions to apply to the dependency. Note that this 
collection denotes the complete set of
+     * exclusions for the dependency, i.e. the dependency manager controls 
whether any existing exclusions get merged
+     * with information from dependency management or overridden by it.
+     * 
+     * @return The new exclusions or {@code null} if the exclusions are not 
managed and the existing dependency
+     *         exclusions should remain unchanged.
+     */
+    public Collection<Exclusion> getExclusions()
+    {
+        return exclusions;
+    }
+
+    /**
+     * Sets the new exclusions to apply to the dependency. Note that this 
collection denotes the complete set of
+     * exclusions for the dependency, i.e. the dependency manager controls 
whether any existing exclusions get merged
+     * with information from dependency management or overridden by it.
+     * 
+     * @param exclusions The new exclusions, may be {@code null} if the 
exclusions are not managed.
+     * @return This management update for chaining, never {@code null}.
+     */
+    public DependencyManagement setExclusions( Collection<Exclusion> 
exclusions )
+    {
+        this.exclusions = exclusions;
+        return this;
+    }
+
+    /**
+     * Gets the new properties to apply to the dependency. Note that this map 
denotes the complete set of properties,
+     * i.e. the dependency manager controls whether any existing properties 
get merged with the information from
+     * dependency management or overridden by it.
+     * 
+     * @return The new artifact properties or {@code null} if the properties 
are not managed and the existing properties
+     *         should remain unchanged.
+     */
+    public Map<String, String> getProperties()
+    {
+        return properties;
+    }
+
+    /**
+     * Sets the new properties to apply to the dependency. Note that this map 
denotes the complete set of properties,
+     * i.e. the dependency manager controls whether any existing properties 
get merged with the information from
+     * dependency management or overridden by it.
+     * 
+     * @param properties The new artifact properties, may be {@code null} if 
the properties are not managed.
+     * @return This management update for chaining, never {@code null}.
+     */
+    public DependencyManagement setProperties( Map<String, String> properties )
+    {
+        this.properties = properties;
+        return this;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManager.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManager.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManager.java
new file mode 100644
index 0000000..993e388
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManager.java
@@ -0,0 +1,57 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.graph.Dependency;
+
+/**
+ * Applies dependency management to the dependencies of a dependency node.
+ * <p>
+ * <strong>Note:</strong> Implementations must be stateless.
+ * <p>
+ * <em>Warning:</em> This hook is called from a hot spot and therefore 
implementations should pay attention to
+ * performance. Among others, implementations should provide a semantic {@link 
Object#equals(Object) equals()} method.
+ * 
+ * @see org.eclipse.aether.RepositorySystemSession#getDependencyManager()
+ * @see 
org.eclipse.aether.RepositorySystem#collectDependencies(org.eclipse.aether.RepositorySystemSession,
+ *      CollectRequest)
+ */
+public interface DependencyManager
+{
+
+    /**
+     * Applies dependency management to the specified dependency.
+     * 
+     * @param dependency The dependency to manage, must not be {@code null}.
+     * @return The management update to apply to the dependency or {@code 
null} if the dependency is not managed at all.
+     */
+    DependencyManagement manageDependency( Dependency dependency );
+
+    /**
+     * Derives a dependency manager for the specified collection context. When 
calculating the child manager,
+     * 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 manager for the dependencies of the target node 
or {@code null} if dependency management
+     *         should no longer be applied.
+     */
+    DependencyManager deriveChildManager( DependencyCollectionContext context 
);
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencySelector.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencySelector.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencySelector.java
new file mode 100644
index 0000000..b257ffa
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencySelector.java
@@ -0,0 +1,58 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.graph.Dependency;
+
+/**
+ * Decides what dependencies to include in the dependency graph.
+ * <p>
+ * <strong>Note:</strong> Implementations must be stateless.
+ * <p>
+ * <em>Warning:</em> This hook is called from a hot spot and therefore 
implementations should pay attention to
+ * performance. Among others, implementations should provide a semantic {@link 
Object#equals(Object) equals()} method.
+ * 
+ * @see org.eclipse.aether.RepositorySystemSession#getDependencySelector()
+ * @see 
org.eclipse.aether.RepositorySystem#collectDependencies(org.eclipse.aether.RepositorySystemSession,
+ *      CollectRequest)
+ */
+public interface DependencySelector
+{
+
+    /**
+     * 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.
+     */
+    boolean selectDependency( Dependency dependency );
+
+    /**
+     * 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 or {@code null} if 
dependencies should be unconditionally
+     *         included in the sub graph.
+     */
+    DependencySelector deriveChildSelector( DependencyCollectionContext 
context );
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyTraverser.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyTraverser.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyTraverser.java
new file mode 100644
index 0000000..be1887b
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyTraverser.java
@@ -0,0 +1,59 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.graph.Dependency;
+
+/**
+ * Decides whether the dependencies of a dependency node should be traversed 
as well.
+ * <p>
+ * <strong>Note:</strong> Implementations must be stateless.
+ * <p>
+ * <em>Warning:</em> This hook is called from a hot spot and therefore 
implementations should pay attention to
+ * performance. Among others, implementations should provide a semantic {@link 
Object#equals(Object) equals()} method.
+ * 
+ * @see org.eclipse.aether.RepositorySystemSession#getDependencyTraverser()
+ * @see 
org.eclipse.aether.RepositorySystem#collectDependencies(org.eclipse.aether.RepositorySystemSession,
+ *      CollectRequest)
+ */
+public interface DependencyTraverser
+{
+
+    /**
+     * Decides whether the dependencies of the specified dependency should be 
traversed.
+     * 
+     * @param dependency The dependency to check, must not be {@code null}.
+     * @return {@code true} if the dependency graph builder should recurse 
into the specified dependency and process its
+     *         dependencies, {@code false} otherwise.
+     */
+    boolean traverseDependency( Dependency dependency );
+
+    /**
+     * Derives a dependency traverser that will be used to decide whether the 
transitive dependencies of the dependency
+     * given in the collection context shall be traversed. When calculating 
the child traverser, 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 traverser for the target node or {@code null} if 
dependencies should be unconditionally
+     *         traversed in the sub graph.
+     */
+    DependencyTraverser deriveChildTraverser( DependencyCollectionContext 
context );
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/UnsolvableVersionConflictException.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/UnsolvableVersionConflictException.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/UnsolvableVersionConflictException.java
new file mode 100644
index 0000000..54a7004
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/UnsolvableVersionConflictException.java
@@ -0,0 +1,142 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.eclipse.aether.RepositoryException;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.graph.DependencyNode;
+import org.eclipse.aether.version.VersionConstraint;
+
+/**
+ * Thrown in case of an unsolvable conflict between different version 
constraints for a dependency.
+ */
+public class UnsolvableVersionConflictException
+    extends RepositoryException
+{
+
+    private final transient Collection<String> versions;
+
+    private final transient Collection<? extends List<? extends 
DependencyNode>> paths;
+
+    /**
+     * Creates a new exception with the specified paths to conflicting nodes 
in the dependency graph.
+     * 
+     * @param paths The paths to the dependency nodes that participate in the 
version conflict, may be {@code null}.
+     */
+    public UnsolvableVersionConflictException( Collection<? extends List<? 
extends DependencyNode>> paths )
+    {
+        super( "Could not resolve version conflict among " + toPaths( paths ) 
);
+        if ( paths == null )
+        {
+            this.paths = Collections.emptyList();
+            this.versions = Collections.emptyList();
+        }
+        else
+        {
+            this.paths = paths;
+            this.versions = new LinkedHashSet<String>();
+            for ( List<? extends DependencyNode> path : paths )
+            {
+                VersionConstraint constraint = path.get( path.size() - 1 
).getVersionConstraint();
+                if ( constraint != null && constraint.getRange() != null )
+                {
+                    versions.add( constraint.toString() );
+                }
+            }
+        }
+    }
+
+    private static String toPaths( Collection<? extends List<? extends 
DependencyNode>> paths )
+    {
+        String result = "";
+
+        if ( paths != null )
+        {
+            Collection<String> strings = new LinkedHashSet<String>();
+
+            for ( List<? extends DependencyNode> path : paths )
+            {
+                strings.add( toPath( path ) );
+            }
+
+            result = strings.toString();
+        }
+
+        return result;
+    }
+
+    private static String toPath( List<? extends DependencyNode> path )
+    {
+        StringBuilder buffer = new StringBuilder( 256 );
+
+        for ( Iterator<? extends DependencyNode> it = path.iterator(); 
it.hasNext(); )
+        {
+            DependencyNode node = it.next();
+            if ( node.getDependency() == null )
+            {
+                continue;
+            }
+
+            Artifact artifact = node.getDependency().getArtifact();
+            buffer.append( artifact.getGroupId() );
+            buffer.append( ':' ).append( artifact.getArtifactId() );
+            buffer.append( ':' ).append( artifact.getExtension() );
+            if ( artifact.getClassifier().length() > 0 )
+            {
+                buffer.append( ':' ).append( artifact.getClassifier() );
+            }
+            buffer.append( ':' ).append( node.getVersionConstraint() );
+
+            if ( it.hasNext() )
+            {
+                buffer.append( " -> " );
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    /**
+     * Gets the paths leading to the conflicting dependencies.
+     * 
+     * @return The (read-only) paths leading to the conflicting dependencies, 
never {@code null}.
+     */
+    public Collection<? extends List<? extends DependencyNode>> getPaths()
+    {
+        return paths;
+    }
+
+    /**
+     * Gets the conflicting version constraints of the dependency.
+     * 
+     * @return The (read-only) conflicting version constraints, never {@code 
null}.
+     */
+    public Collection<String> getVersions()
+    {
+        return versions;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilter.java
----------------------------------------------------------------------
diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilter.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilter.java
new file mode 100644
index 0000000..fb36747
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilter.java
@@ -0,0 +1,135 @@
+package org.eclipse.aether.collection;
+
+/*
+ * 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.Iterator;
+import java.util.List;
+
+import org.eclipse.aether.RepositoryException;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.repository.ArtifactRepository;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.version.Version;
+import org.eclipse.aether.version.VersionConstraint;
+
+/**
+ * Decides which versions matching a version range should actually be 
considered for the dependency graph. The version
+ * filter is not invoked for dependencies that do not declare a version range 
but a single version.
+ * <p>
+ * <strong>Note:</strong> Implementations must be stateless.
+ * <p>
+ * <em>Warning:</em> This hook is called from a hot spot and therefore 
implementations should pay attention to
+ * performance. Among others, implementations should provide a semantic {@link 
Object#equals(Object) equals()} method.
+ * 
+ * @see org.eclipse.aether.RepositorySystemSession#getVersionFilter()
+ * @see 
org.eclipse.aether.RepositorySystem#collectDependencies(org.eclipse.aether.RepositorySystemSession,
+ *      CollectRequest)
+ */
+public interface VersionFilter
+{
+
+    /**
+     * A context used during version filtering to hold relevant data.
+     * 
+     * @noimplement This interface is not intended to be implemented by 
clients.
+     * @noextend This interface is not intended to be extended by clients.
+     */
+    interface VersionFilterContext
+        extends Iterable<Version>
+    {
+
+        /**
+         * Gets the repository system session during which the version 
filtering happens.
+         * 
+         * @return The repository system session, never {@code null}.
+         */
+        RepositorySystemSession getSession();
+
+        /**
+         * Gets the dependency whose version range is being filtered.
+         * 
+         * @return The dependency, never {@code null}.
+         */
+        Dependency getDependency();
+
+        /**
+         * Gets the total number of available versions. This count reflects 
any removals made during version filtering.
+         * 
+         * @return The total number of available versions.
+         */
+        int getCount();
+
+        /**
+         * Gets an iterator over the available versions of the dependency. The 
iterator returns versions in ascending
+         * order. Use {@link Iterator#remove()} to exclude a version from 
further consideration in the dependency graph.
+         * 
+         * @return The iterator of available versions, never {@code null}.
+         */
+        Iterator<Version> iterator();
+
+        /**
+         * Gets the version constraint that was parsed from the dependency's 
version string.
+         * 
+         * @return The parsed version constraint, never {@code null}.
+         */
+        VersionConstraint getVersionConstraint();
+
+        /**
+         * Gets the repository from which the specified version was resolved.
+         * 
+         * @param version The version whose source repository should be 
retrieved, must not be {@code null}.
+         * @return The repository from which the version was resolved or 
{@code null} if unknown.
+         */
+        ArtifactRepository getRepository( Version version );
+
+        /**
+         * Gets the remote repositories from which the versions were resolved.
+         * 
+         * @return The (read-only) list of repositories, never {@code null}.
+         */
+        List<RemoteRepository> getRepositories();
+
+    }
+
+    /**
+     * Filters the available versions for a given dependency. Implementations 
will usually call
+     * {@link VersionFilterContext#iterator() context.iterator()} to inspect 
the available versions and use
+     * {@link java.util.Iterator#remove()} to delete unacceptable versions. If 
no versions remain after all filtering
+     * has been performed, the dependency collection process will 
automatically fail, i.e. implementations need not
+     * handle this situation on their own.
+     * 
+     * @param context The version filter context, must not be {@code null}.
+     * @throws RepositoryException If the filtering could not be performed.
+     */
+    void filterVersions( VersionFilterContext context )
+        throws RepositoryException;
+
+    /**
+     * Derives a version filter for the specified collection context. The 
derived filter will be used to handle version
+     * ranges encountered in child dependencies of the current node. When 
calculating the child filter, 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 version filter for the target node or {@code null} if 
versions should not be filtered any more.
+     */
+    VersionFilter deriveChildFilter( DependencyCollectionContext context );
+
+}

Reply via email to