You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by hb...@apache.org on 2017/01/15 19:23:39 UTC
[05/53] [abbrv] [partial] maven-resolver git commit: [MNG-6007]
renamed Aether to Maven Artifact Resolver
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/package-info.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/package-info.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/package-info.java
new file mode 100644
index 0000000..414629f
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/package-info.java
@@ -0,0 +1,25 @@
+// 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 types and extension points for collecting the transitive dependencies of an artifact and building a dependency
+ * graph.
+ */
+package org.eclipse.aether.collection;
+
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeployRequest.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeployRequest.java b/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeployRequest.java
new file mode 100644
index 0000000..637f47d
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeployRequest.java
@@ -0,0 +1,202 @@
+package org.eclipse.aether.deployment;
+
+/*
+ * 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 org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.RequestTrace;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.metadata.Metadata;
+import org.eclipse.aether.repository.RemoteRepository;
+
+/**
+ * A request to deploy artifacts and their accompanying metadata into the a remote repository.
+ *
+ * @see RepositorySystem#deploy(RepositorySystemSession, DeployRequest)
+ */
+public final class DeployRequest
+{
+
+ private Collection<Artifact> artifacts = Collections.emptyList();
+
+ private Collection<Metadata> metadata = Collections.emptyList();
+
+ private RemoteRepository repository;
+
+ private RequestTrace trace;
+
+ /**
+ * Creates an uninitialized request.
+ */
+ public DeployRequest()
+ {
+ }
+
+ /**
+ * Gets the artifact to deploy.
+ *
+ * @return The artifacts to deploy, never {@code null}.
+ */
+ public Collection<Artifact> getArtifacts()
+ {
+ return artifacts;
+ }
+
+ /**
+ * Sets the artifacts to deploy.
+ *
+ * @param artifacts The artifacts to deploy, may be {@code null}.
+ * @return This request for chaining, never {@code null}.
+ */
+ public DeployRequest setArtifacts( Collection<Artifact> artifacts )
+ {
+ if ( artifacts == null )
+ {
+ this.artifacts = Collections.emptyList();
+ }
+ else
+ {
+ this.artifacts = artifacts;
+ }
+ return this;
+ }
+
+ /**
+ * Adds the specified artifacts for deployment.
+ *
+ * @param artifact The artifact to add, may be {@code null}.
+ * @return This request for chaining, never {@code null}.
+ */
+ public DeployRequest addArtifact( Artifact artifact )
+ {
+ if ( artifact != null )
+ {
+ if ( artifacts.isEmpty() )
+ {
+ artifacts = new ArrayList<Artifact>();
+ }
+ artifacts.add( artifact );
+ }
+ return this;
+ }
+
+ /**
+ * Gets the metadata to deploy.
+ *
+ * @return The metadata to deploy, never {@code null}.
+ */
+ public Collection<Metadata> getMetadata()
+ {
+ return metadata;
+ }
+
+ /**
+ * Sets the metadata to deploy.
+ *
+ * @param metadata The metadata to deploy, may be {@code null}.
+ * @return This request for chaining, never {@code null}.
+ */
+ public DeployRequest setMetadata( Collection<Metadata> metadata )
+ {
+ if ( metadata == null )
+ {
+ this.metadata = Collections.emptyList();
+ }
+ else
+ {
+ this.metadata = metadata;
+ }
+ return this;
+ }
+
+ /**
+ * Adds the specified metadata for deployment.
+ *
+ * @param metadata The metadata to add, may be {@code null}.
+ * @return This request for chaining, never {@code null}.
+ */
+ public DeployRequest addMetadata( Metadata metadata )
+ {
+ if ( metadata != null )
+ {
+ if ( this.metadata.isEmpty() )
+ {
+ this.metadata = new ArrayList<Metadata>();
+ }
+ this.metadata.add( metadata );
+ }
+ return this;
+ }
+
+ /**
+ * Gets the repository to deploy to.
+ *
+ * @return The repository to deploy to or {@code null} if not set.
+ */
+ public RemoteRepository getRepository()
+ {
+ return repository;
+ }
+
+ /**
+ * Sets the repository to deploy to.
+ *
+ * @param repository The repository to deploy to, may be {@code null}.
+ * @return This request for chaining, never {@code null}.
+ */
+ public DeployRequest setRepository( RemoteRepository repository )
+ {
+ this.repository = repository;
+ 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 DeployRequest setTrace( RequestTrace trace )
+ {
+ this.trace = trace;
+ return this;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getArtifacts() + ", " + getMetadata() + " > " + getRepository();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeployResult.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeployResult.java b/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeployResult.java
new file mode 100644
index 0000000..f75705e
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeployResult.java
@@ -0,0 +1,174 @@
+package org.eclipse.aether.deployment;
+
+/*
+ * 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 org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.metadata.Metadata;
+
+/**
+ * The result of deploying artifacts and their accompanying metadata into the a remote repository.
+ *
+ * @see RepositorySystem#deploy(RepositorySystemSession, DeployRequest)
+ */
+public final class DeployResult
+{
+
+ private final DeployRequest request;
+
+ private Collection<Artifact> artifacts;
+
+ private Collection<Metadata> metadata;
+
+ /**
+ * Creates a new result for the specified request.
+ *
+ * @param request The deployment request, must not be {@code null}.
+ */
+ public DeployResult( DeployRequest request )
+ {
+ if ( request == null )
+ {
+ throw new IllegalArgumentException( "deploy request has not been specified" );
+ }
+ this.request = request;
+ artifacts = Collections.emptyList();
+ metadata = Collections.emptyList();
+ }
+
+ /**
+ * Gets the deploy request that was made.
+ *
+ * @return The deploy request, never {@code null}.
+ */
+ public DeployRequest getRequest()
+ {
+ return request;
+ }
+
+ /**
+ * Gets the artifacts that got deployed.
+ *
+ * @return The deployed artifacts, never {@code null}.
+ */
+ public Collection<Artifact> getArtifacts()
+ {
+ return artifacts;
+ }
+
+ /**
+ * Sets the artifacts that got deployed.
+ *
+ * @param artifacts The deployed artifacts, may be {@code null}.
+ * @return This result for chaining, never {@code null}.
+ */
+ public DeployResult setArtifacts( Collection<Artifact> artifacts )
+ {
+ if ( artifacts == null )
+ {
+ this.artifacts = Collections.emptyList();
+ }
+ else
+ {
+ this.artifacts = artifacts;
+ }
+ return this;
+ }
+
+ /**
+ * Adds the specified artifacts to the result.
+ *
+ * @param artifact The deployed artifact to add, may be {@code null}.
+ * @return This result for chaining, never {@code null}.
+ */
+ public DeployResult addArtifact( Artifact artifact )
+ {
+ if ( artifact != null )
+ {
+ if ( artifacts.isEmpty() )
+ {
+ artifacts = new ArrayList<Artifact>();
+ }
+ artifacts.add( artifact );
+ }
+ return this;
+ }
+
+ /**
+ * Gets the metadata that got deployed. Note that due to automatically generated metadata, there might have been
+ * more metadata deployed than originally specified in the deploy request.
+ *
+ * @return The deployed metadata, never {@code null}.
+ */
+ public Collection<Metadata> getMetadata()
+ {
+ return metadata;
+ }
+
+ /**
+ * Sets the metadata that got deployed.
+ *
+ * @param metadata The deployed metadata, may be {@code null}.
+ * @return This result for chaining, never {@code null}.
+ */
+ public DeployResult setMetadata( Collection<Metadata> metadata )
+ {
+ if ( metadata == null )
+ {
+ this.metadata = Collections.emptyList();
+ }
+ else
+ {
+ this.metadata = metadata;
+ }
+ return this;
+ }
+
+ /**
+ * Adds the specified metadata to this result.
+ *
+ * @param metadata The deployed metadata to add, may be {@code null}.
+ * @return This result for chaining, never {@code null}.
+ */
+ public DeployResult addMetadata( Metadata metadata )
+ {
+ if ( metadata != null )
+ {
+ if ( this.metadata.isEmpty() )
+ {
+ this.metadata = new ArrayList<Metadata>();
+ }
+ this.metadata.add( metadata );
+ }
+ return this;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getArtifacts() + ", " + getMetadata();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeploymentException.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeploymentException.java b/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeploymentException.java
new file mode 100644
index 0000000..53252ba
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/DeploymentException.java
@@ -0,0 +1,52 @@
+package org.eclipse.aether.deployment;
+
+/*
+ * 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 a deployment error like authentication failure.
+ */
+public class DeploymentException
+ extends RepositoryException
+{
+
+ /**
+ * Creates a new exception with the specified detail message.
+ *
+ * @param message The detail message, may be {@code null}.
+ */
+ public DeploymentException( String message )
+ {
+ super( message );
+ }
+
+ /**
+ * Creates a new exception with the specified detail message and cause.
+ *
+ * @param message The detail message, may be {@code null}.
+ * @param cause The exception that caused this one, may be {@code null}.
+ */
+ public DeploymentException( String message, Throwable cause )
+ {
+ super( message, cause );
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/package-info.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/package-info.java b/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/package-info.java
new file mode 100644
index 0000000..dc50c21
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/deployment/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 types supporting the publishing of artifacts to a remote repository.
+ */
+package org.eclipse.aether.deployment;
+
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DefaultDependencyNode.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DefaultDependencyNode.java b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DefaultDependencyNode.java
new file mode 100644
index 0000000..66a60c6
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DefaultDependencyNode.java
@@ -0,0 +1,368 @@
+package org.eclipse.aether.graph;
+
+/*
+ * 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.List;
+import java.util.Map;
+
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.version.Version;
+import org.eclipse.aether.version.VersionConstraint;
+
+/**
+ * A node within a dependency graph.
+ */
+public final class DefaultDependencyNode
+ implements DependencyNode
+{
+
+ private List<DependencyNode> children;
+
+ private Dependency dependency;
+
+ private Artifact artifact;
+
+ private List<? extends Artifact> relocations;
+
+ private Collection<? extends Artifact> aliases;
+
+ private VersionConstraint versionConstraint;
+
+ private Version version;
+
+ private byte managedBits;
+
+ private List<RemoteRepository> repositories;
+
+ private String context;
+
+ private Map<Object, Object> data;
+
+ /**
+ * Creates a new node with the specified dependency.
+ *
+ * @param dependency The dependency associated with this node, may be {@code null} for a root node.
+ */
+ public DefaultDependencyNode( Dependency dependency )
+ {
+ this.dependency = dependency;
+ artifact = ( dependency != null ) ? dependency.getArtifact() : null;
+ children = new ArrayList<DependencyNode>( 0 );
+ aliases = relocations = Collections.emptyList();
+ repositories = Collections.emptyList();
+ context = "";
+ data = Collections.emptyMap();
+ }
+
+ /**
+ * Creates a new root node with the specified artifact as its label. Note that the new node has no dependency, i.e.
+ * {@link #getDependency()} will return {@code null}. Put differently, the specified artifact will not be subject to
+ * dependency collection/resolution.
+ *
+ * @param artifact The artifact to use as label for this node, may be {@code null}.
+ */
+ public DefaultDependencyNode( Artifact artifact )
+ {
+ this.artifact = artifact;
+ children = new ArrayList<DependencyNode>( 0 );
+ aliases = relocations = Collections.emptyList();
+ repositories = Collections.emptyList();
+ context = "";
+ data = Collections.emptyMap();
+ }
+
+ /**
+ * Creates a mostly shallow clone of the specified node. The new node has its own copy of any custom data and
+ * initially no children.
+ *
+ * @param node The node to copy, must not be {@code null}.
+ */
+ public DefaultDependencyNode( DependencyNode node )
+ {
+ dependency = node.getDependency();
+ artifact = node.getArtifact();
+ children = new ArrayList<DependencyNode>( 0 );
+ setAliases( node.getAliases() );
+ setRequestContext( node.getRequestContext() );
+ setManagedBits( node.getManagedBits() );
+ setRelocations( node.getRelocations() );
+ setRepositories( node.getRepositories() );
+ setVersion( node.getVersion() );
+ setVersionConstraint( node.getVersionConstraint() );
+ Map<?, ?> data = node.getData();
+ setData( data.isEmpty() ? null : new HashMap<Object, Object>( data ) );
+ }
+
+ public List<DependencyNode> getChildren()
+ {
+ return children;
+ }
+
+ public void setChildren( List<DependencyNode> children )
+ {
+ if ( children == null )
+ {
+ this.children = new ArrayList<DependencyNode>( 0 );
+ }
+ else
+ {
+ this.children = children;
+ }
+ }
+
+ public Dependency getDependency()
+ {
+ return dependency;
+ }
+
+ public Artifact getArtifact()
+ {
+ return artifact;
+ }
+
+ public void setArtifact( Artifact artifact )
+ {
+ if ( dependency == null )
+ {
+ throw new UnsupportedOperationException( "node does not have a dependency" );
+ }
+ dependency = dependency.setArtifact( artifact );
+ this.artifact = dependency.getArtifact();
+ }
+
+ public List<? extends Artifact> getRelocations()
+ {
+ return relocations;
+ }
+
+ /**
+ * Sets the sequence of relocations that was followed to resolve this dependency's artifact.
+ *
+ * @param relocations The sequence of relocations, may be {@code null}.
+ */
+ public void setRelocations( List<? extends Artifact> relocations )
+ {
+ if ( relocations == null || relocations.isEmpty() )
+ {
+ this.relocations = Collections.emptyList();
+ }
+ else
+ {
+ this.relocations = relocations;
+ }
+ }
+
+ public Collection<? extends Artifact> getAliases()
+ {
+ return aliases;
+ }
+
+ /**
+ * Sets the known aliases for this dependency's artifact.
+ *
+ * @param aliases The known aliases, may be {@code null}.
+ */
+ public void setAliases( Collection<? extends Artifact> aliases )
+ {
+ if ( aliases == null || aliases.isEmpty() )
+ {
+ this.aliases = Collections.emptyList();
+ }
+ else
+ {
+ this.aliases = aliases;
+ }
+ }
+
+ public VersionConstraint getVersionConstraint()
+ {
+ return versionConstraint;
+ }
+
+ /**
+ * Sets the version constraint that was parsed from the dependency's version declaration.
+ *
+ * @param versionConstraint The version constraint for this node, may be {@code null}.
+ */
+ public void setVersionConstraint( VersionConstraint versionConstraint )
+ {
+ this.versionConstraint = versionConstraint;
+ }
+
+ public Version getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * Sets the version that was selected for the dependency's target artifact.
+ *
+ * @param version The parsed version, may be {@code null}.
+ */
+ public void setVersion( Version version )
+ {
+ this.version = version;
+ }
+
+ public void setScope( String scope )
+ {
+ if ( dependency == null )
+ {
+ throw new UnsupportedOperationException( "node does not have a dependency" );
+ }
+ dependency = dependency.setScope( scope );
+ }
+
+ public void setOptional( Boolean optional )
+ {
+ if ( dependency == null )
+ {
+ throw new UnsupportedOperationException( "node does not have a dependency" );
+ }
+ dependency = dependency.setOptional( optional );
+ }
+
+ public int getManagedBits()
+ {
+ return managedBits;
+ }
+
+ /**
+ * Sets a bit field indicating which attributes of this node were subject to dependency management.
+ *
+ * @param managedBits The bit field indicating the managed attributes or {@code 0} if dependency management wasn't
+ * applied.
+ */
+ public void setManagedBits( int managedBits )
+ {
+ this.managedBits = (byte) ( managedBits & 0x1F );
+ }
+
+ public List<RemoteRepository> getRepositories()
+ {
+ return repositories;
+ }
+
+ /**
+ * Sets the remote repositories from which this node's artifact shall be resolved.
+ *
+ * @param repositories The remote repositories to use for artifact resolution, may be {@code null}.
+ */
+ public void setRepositories( List<RemoteRepository> repositories )
+ {
+ if ( repositories == null || repositories.isEmpty() )
+ {
+ this.repositories = Collections.emptyList();
+ }
+ else
+ {
+ this.repositories = repositories;
+ }
+ }
+
+ public String getRequestContext()
+ {
+ return context;
+ }
+
+ public void setRequestContext( String context )
+ {
+ this.context = ( context != null ) ? context : "";
+ }
+
+ public Map<Object, Object> getData()
+ {
+ return data;
+ }
+
+ public void setData( Map<Object, Object> data )
+ {
+ if ( data == null )
+ {
+ this.data = Collections.emptyMap();
+ }
+ else
+ {
+ this.data = data;
+ }
+ }
+
+ public void setData( 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>( 1, 2 ); // nodes can be numerous so let's be space conservative
+ }
+ data.put( key, value );
+ }
+ }
+
+ public boolean accept( DependencyVisitor visitor )
+ {
+ if ( visitor.visitEnter( this ) )
+ {
+ for ( DependencyNode child : children )
+ {
+ if ( !child.accept( visitor ) )
+ {
+ break;
+ }
+ }
+ }
+
+ return visitor.visitLeave( this );
+ }
+
+ @Override
+ public String toString()
+ {
+ Dependency dep = getDependency();
+ if ( dep == null )
+ {
+ return String.valueOf( getArtifact() );
+ }
+ return dep.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/graph/Dependency.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/Dependency.java b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/Dependency.java
new file mode 100644
index 0000000..b9e7cb4
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/Dependency.java
@@ -0,0 +1,330 @@
+package org.eclipse.aether.graph;
+
+/*
+ * 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.AbstractSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.eclipse.aether.artifact.Artifact;
+
+/**
+ * A dependency to some 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 Dependency
+{
+
+ private final Artifact artifact;
+
+ private final String scope;
+
+ private final Boolean optional;
+
+ private final Set<Exclusion> exclusions;
+
+ /**
+ * Creates a mandatory dependency on the specified artifact with the given scope.
+ *
+ * @param artifact The artifact being depended on, must not be {@code null}.
+ * @param scope The scope of the dependency, may be {@code null}.
+ */
+ public Dependency( Artifact artifact, String scope )
+ {
+ this( artifact, scope, false );
+ }
+
+ /**
+ * Creates a dependency on the specified artifact with the given scope.
+ *
+ * @param artifact The artifact being depended on, must not be {@code null}.
+ * @param scope The scope of the dependency, may be {@code null}.
+ * @param optional A flag whether the dependency is optional or mandatory, may be {@code null}.
+ */
+ public Dependency( Artifact artifact, String scope, Boolean optional )
+ {
+ this( artifact, scope, optional, null );
+ }
+
+ /**
+ * Creates a dependency on the specified artifact with the given scope and exclusions.
+ *
+ * @param artifact The artifact being depended on, must not be {@code null}.
+ * @param scope The scope of the dependency, may be {@code null}.
+ * @param optional A flag whether the dependency is optional or mandatory, may be {@code null}.
+ * @param exclusions The exclusions that apply to transitive dependencies, may be {@code null} if none.
+ */
+ public Dependency( Artifact artifact, String scope, Boolean optional, Collection<Exclusion> exclusions )
+ {
+ this( artifact, scope, Exclusions.copy( exclusions ), optional );
+ }
+
+ private Dependency( Artifact artifact, String scope, Set<Exclusion> exclusions, Boolean optional )
+ {
+ // NOTE: This constructor assumes immutability of the provided exclusion collection, for internal use only
+ if ( artifact == null )
+ {
+ throw new IllegalArgumentException( "no artifact specified for dependency" );
+ }
+ this.artifact = artifact;
+ this.scope = ( scope != null ) ? scope : "";
+ this.optional = optional;
+ this.exclusions = exclusions;
+ }
+
+ /**
+ * Gets the artifact being depended on.
+ *
+ * @return The artifact, never {@code null}.
+ */
+ public Artifact getArtifact()
+ {
+ return artifact;
+ }
+
+ /**
+ * Sets the artifact being depended on.
+ *
+ * @param artifact The artifact, must not be {@code null}.
+ * @return The new dependency, never {@code null}.
+ */
+ public Dependency setArtifact( Artifact artifact )
+ {
+ if ( this.artifact.equals( artifact ) )
+ {
+ return this;
+ }
+ return new Dependency( artifact, scope, exclusions, optional );
+ }
+
+ /**
+ * Gets the scope of the dependency. The scope defines in which context this dependency is relevant.
+ *
+ * @return The scope or an empty string if not set, never {@code null}.
+ */
+ public String getScope()
+ {
+ return scope;
+ }
+
+ /**
+ * Sets the scope of the dependency, e.g. "compile".
+ *
+ * @param scope The scope of the dependency, may be {@code null}.
+ * @return The new dependency, never {@code null}.
+ */
+ public Dependency setScope( String scope )
+ {
+ if ( this.scope.equals( scope ) || ( scope == null && this.scope.length() <= 0 ) )
+ {
+ return this;
+ }
+ return new Dependency( artifact, scope, exclusions, optional );
+ }
+
+ /**
+ * Indicates whether this dependency is optional or not. Optional dependencies can be ignored in some contexts.
+ *
+ * @return {@code true} if the dependency is (definitively) optional, {@code false} otherwise.
+ */
+ public boolean isOptional()
+ {
+ return Boolean.TRUE.equals( optional );
+ }
+
+ /**
+ * Gets the optional flag for the dependency. Note: Most clients will usually call {@link #isOptional()} to
+ * determine the optional flag, this method is for advanced use cases where three-valued logic is required.
+ *
+ * @return The optional flag or {@code null} if unspecified.
+ */
+ public Boolean getOptional()
+ {
+ return optional;
+ }
+
+ /**
+ * Sets the optional flag for the dependency.
+ *
+ * @param optional {@code true} if the dependency is optional, {@code false} if the dependency is mandatory, may be
+ * {@code null} if unspecified.
+ * @return The new dependency, never {@code null}.
+ */
+ public Dependency setOptional( Boolean optional )
+ {
+ if ( eq( this.optional, optional ) )
+ {
+ return this;
+ }
+ return new Dependency( artifact, scope, exclusions, optional );
+ }
+
+ /**
+ * Gets the exclusions for this dependency. Exclusions can be used to remove transitive dependencies during
+ * resolution.
+ *
+ * @return The (read-only) exclusions, never {@code null}.
+ */
+ public Collection<Exclusion> getExclusions()
+ {
+ return exclusions;
+ }
+
+ /**
+ * Sets the exclusions for the dependency.
+ *
+ * @param exclusions The exclusions, may be {@code null}.
+ * @return The new dependency, never {@code null}.
+ */
+ public Dependency setExclusions( Collection<Exclusion> exclusions )
+ {
+ if ( hasEquivalentExclusions( exclusions ) )
+ {
+ return this;
+ }
+ return new Dependency( artifact, scope, optional, exclusions );
+ }
+
+ private boolean hasEquivalentExclusions( Collection<Exclusion> exclusions )
+ {
+ if ( exclusions == null || exclusions.isEmpty() )
+ {
+ return this.exclusions.isEmpty();
+ }
+ if ( exclusions instanceof Set )
+ {
+ return this.exclusions.equals( exclusions );
+ }
+ return exclusions.size() >= this.exclusions.size() && this.exclusions.containsAll( exclusions )
+ && exclusions.containsAll( this.exclusions );
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.valueOf( getArtifact() ) + " (" + getScope() + ( isOptional() ? "?" : "" ) + ")";
+ }
+
+ @Override
+ public boolean equals( Object obj )
+ {
+ if ( obj == this )
+ {
+ return true;
+ }
+ else if ( obj == null || !getClass().equals( obj.getClass() ) )
+ {
+ return false;
+ }
+
+ Dependency that = (Dependency) obj;
+
+ return artifact.equals( that.artifact ) && scope.equals( that.scope ) && eq( optional, that.optional )
+ && exclusions.equals( that.exclusions );
+ }
+
+ private static <T> boolean eq( T o1, T o2 )
+ {
+ return ( o1 != null ) ? o1.equals( o2 ) : o2 == null;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 17;
+ hash = hash * 31 + artifact.hashCode();
+ hash = hash * 31 + scope.hashCode();
+ hash = hash * 31 + ( optional != null ? optional.hashCode() : 0 );
+ hash = hash * 31 + exclusions.size();
+ return hash;
+ }
+
+ private static class Exclusions
+ extends AbstractSet<Exclusion>
+ {
+
+ private final Exclusion[] exclusions;
+
+ public static Set<Exclusion> copy( Collection<Exclusion> exclusions )
+ {
+ if ( exclusions == null || exclusions.isEmpty() )
+ {
+ return Collections.emptySet();
+ }
+ return new Exclusions( exclusions );
+ }
+
+ private Exclusions( Collection<Exclusion> exclusions )
+ {
+ if ( exclusions.size() > 1 && !( exclusions instanceof Set ) )
+ {
+ exclusions = new LinkedHashSet<Exclusion>( exclusions );
+ }
+ this.exclusions = exclusions.toArray( new Exclusion[exclusions.size()] );
+ }
+
+ @Override
+ public Iterator<Exclusion> iterator()
+ {
+ return new Iterator<Exclusion>()
+ {
+
+ private int cursor = 0;
+
+ public boolean hasNext()
+ {
+ return cursor < exclusions.length;
+ }
+
+ public Exclusion next()
+ {
+ try
+ {
+ Exclusion exclusion = exclusions[cursor];
+ cursor++;
+ return exclusion;
+ }
+ catch ( IndexOutOfBoundsException e )
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ };
+ }
+
+ @Override
+ public int size()
+ {
+ return exclusions.length;
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyCycle.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyCycle.java b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyCycle.java
new file mode 100644
index 0000000..1076ab8
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyCycle.java
@@ -0,0 +1,53 @@
+package org.eclipse.aether.graph;
+
+/*
+ * 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;
+
+/**
+ * A cycle within a dependency graph, that is a sequence of dependencies d_1, d_2, ..., d_n where d_1 and d_n have the
+ * same versionless coordinates. In more practical terms, a cycle occurs when a project directly or indirectly depends
+ * on its own output artifact.
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface DependencyCycle
+{
+
+ /**
+ * Gets the dependencies that lead to the first dependency on the cycle, starting from the root of the dependency
+ * graph.
+ *
+ * @return The (read-only) sequence of dependencies that precedes the cycle in the graph, potentially empty but
+ * never {@code null}.
+ */
+ List<Dependency> getPrecedingDependencies();
+
+ /**
+ * Gets the dependencies that actually form the cycle. For example, a -> b -> c -> a, i.e. the last
+ * dependency in this sequence duplicates the first element and closes the cycle. Hence the length of the cycle is
+ * the size of the returned sequence minus 1.
+ *
+ * @return The (read-only) sequence of dependencies that forms the cycle, never {@code null}.
+ */
+ List<Dependency> getCyclicDependencies();
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyFilter.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyFilter.java b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyFilter.java
new file mode 100644
index 0000000..41776ff
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyFilter.java
@@ -0,0 +1,42 @@
+package org.eclipse.aether.graph;
+
+/*
+ * 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;
+
+/**
+ * A filter to include/exclude dependency nodes during other operations.
+ */
+public interface DependencyFilter
+{
+
+ /**
+ * Indicates whether the specified dependency node shall be included or excluded.
+ *
+ * @param node The dependency node to filter, must not be {@code null}.
+ * @param parents The (read-only) chain of parent nodes that leads to the node to be filtered, must not be
+ * {@code null}. Iterating this (possibly empty) list walks up the dependency graph towards the root
+ * node, i.e. the immediate parent node (if any) is the first node in the list. The size of the list also
+ * denotes the zero-based depth of the filtered node.
+ * @return {@code true} to include the dependency node, {@code false} to exclude it.
+ */
+ boolean accept( DependencyNode node, List<DependencyNode> parents );
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyNode.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyNode.java b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyNode.java
new file mode 100644
index 0000000..2551043
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyNode.java
@@ -0,0 +1,232 @@
+package org.eclipse.aether.graph;
+
+/*
+ * 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.List;
+import java.util.Map;
+
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.version.Version;
+import org.eclipse.aether.version.VersionConstraint;
+
+/**
+ * A node within a dependency graph. To conserve memory, dependency graphs may reuse a given node instance multiple
+ * times to represent reoccurring dependencies. As such clients traversing a dependency graph should be prepared to
+ * discover multiple paths leading to the same node instance unless the input graph is known to be a duplicate-free
+ * tree. <em>Note:</em> Unless otherwise noted, implementation classes are not thread-safe and dependency nodes should
+ * not be mutated by concurrent threads.
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface DependencyNode
+{
+
+ /**
+ * A bit flag indicating the dependency version was subject to dependency management
+ *
+ * @see #getManagedBits()
+ */
+ int MANAGED_VERSION = 0x01;
+
+ /**
+ * A bit flag indicating the dependency scope was subject to dependency management
+ *
+ * @see #getManagedBits()
+ */
+ int MANAGED_SCOPE = 0x02;
+
+ /**
+ * A bit flag indicating the optional flag was subject to dependency management
+ *
+ * @see #getManagedBits()
+ */
+ int MANAGED_OPTIONAL = 0x04;
+
+ /**
+ * A bit flag indicating the artifact properties were subject to dependency management
+ *
+ * @see #getManagedBits()
+ */
+ int MANAGED_PROPERTIES = 0x08;
+
+ /**
+ * A bit flag indicating the exclusions were subject to dependency management
+ *
+ * @see #getManagedBits()
+ */
+ int MANAGED_EXCLUSIONS = 0x10;
+
+ /**
+ * Gets the child nodes of this node. To conserve memory, dependency nodes with equal dependencies may share the
+ * same child list instance. Hence clients mutating the child list need to be aware that these changes might affect
+ * more than this node. Where this is not desired, the child list should be copied before mutation if the client
+ * cannot be sure whether it might be shared with other nodes in the graph.
+ *
+ * @return The child nodes of this node, never {@code null}.
+ */
+ List<DependencyNode> getChildren();
+
+ /**
+ * Sets the child nodes of this node.
+ *
+ * @param children The child nodes, may be {@code null}
+ */
+ void setChildren( List<DependencyNode> children );
+
+ /**
+ * Gets the dependency associated with this node. <em>Note:</em> For dependency graphs that have been constructed
+ * without a root dependency, this method will yield {@code null} when invoked on the graph's root node. The root
+ * node of such graphs may however still have a label as returned by {@link #getArtifact()}.
+ *
+ * @return The dependency or {@code null} if none.
+ */
+ Dependency getDependency();
+
+ /**
+ * Gets the artifact associated with this node. If this node is associated with a dependency, this is equivalent to
+ * {@code getDependency().getArtifact()}. Otherwise the artifact merely provides a label for this node in which case
+ * the artifact must not be subjected to dependency collection/resolution.
+ *
+ * @return The associated artifact or {@code null} if none.
+ */
+ Artifact getArtifact();
+
+ /**
+ * Updates the artifact of the dependency after resolution. The new artifact must have the same coordinates as the
+ * original artifact. This method may only be invoked if this node actually has a dependency, i.e. if
+ * {@link #getDependency()} is not null.
+ *
+ * @param artifact The artifact satisfying the dependency, must not be {@code null}.
+ */
+ void setArtifact( Artifact artifact );
+
+ /**
+ * Gets the sequence of relocations that was followed to resolve the artifact referenced by the dependency.
+ *
+ * @return The (read-only) sequence of relocations, never {@code null}.
+ */
+ List<? extends Artifact> getRelocations();
+
+ /**
+ * Gets the known aliases for this dependency's artifact. An alias can be used to mark a patched rebuild of some
+ * other artifact as such, thereby allowing conflict resolution to consider the patched and the original artifact as
+ * a conflict.
+ *
+ * @return The (read-only) set of known aliases, never {@code null}.
+ */
+ Collection<? extends Artifact> getAliases();
+
+ /**
+ * Gets the version constraint that was parsed from the dependency's version declaration.
+ *
+ * @return The version constraint for this node or {@code null}.
+ */
+ VersionConstraint getVersionConstraint();
+
+ /**
+ * Gets the version that was selected for the dependency's target artifact.
+ *
+ * @return The parsed version or {@code null}.
+ */
+ Version getVersion();
+
+ /**
+ * Sets the scope of the dependency. This method may only be invoked if this node actually has a dependency, i.e. if
+ * {@link #getDependency()} is not null.
+ *
+ * @param scope The scope, may be {@code null}.
+ */
+ void setScope( String scope );
+
+ /**
+ * Sets the optional flag of the dependency. This method may only be invoked if this node actually has a dependency,
+ * i.e. if {@link #getDependency()} is not null.
+ *
+ * @param optional The optional flag, may be {@code null}.
+ */
+ void setOptional( Boolean optional );
+
+ /**
+ * Gets a bit field indicating which attributes of this node were subject to dependency management.
+ *
+ * @return A bit field containing any of the bits {@link #MANAGED_VERSION}, {@link #MANAGED_SCOPE},
+ * {@link #MANAGED_OPTIONAL}, {@link #MANAGED_PROPERTIES} and {@link #MANAGED_EXCLUSIONS} if the
+ * corresponding attribute was set via dependency management.
+ */
+ int getManagedBits();
+
+ /**
+ * Gets the remote repositories from which this node's artifact shall be resolved.
+ *
+ * @return The (read-only) list of remote repositories to use for artifact resolution, never {@code null}.
+ */
+ List<RemoteRepository> getRepositories();
+
+ /**
+ * Gets the request context in which this dependency node was created.
+ *
+ * @return The request context, never {@code null}.
+ */
+ String getRequestContext();
+
+ /**
+ * Sets the request context in which this dependency node was created.
+ *
+ * @param context The context, may be {@code null}.
+ */
+ void setRequestContext( String context );
+
+ /**
+ * Gets the custom data associated with this dependency node. Clients of the repository system can use this data to
+ * annotate dependency nodes with domain-specific information. Note that the returned map is read-only and
+ * {@link #setData(Object, Object)} needs to be used to update the custom data.
+ *
+ * @return The (read-only) key-value mappings, never {@code null}.
+ */
+ Map<?, ?> getData();
+
+ /**
+ * Sets the custom data associated with this dependency node.
+ *
+ * @param data The new custom data, may be {@code null}.
+ */
+ void setData( Map<Object, Object> data );
+
+ /**
+ * Associates the specified dependency node data with the given key. <em>Note:</em> This method must not be called
+ * while {@link #getData()} is being iterated.
+ *
+ * @param key The key under which to store the data, must not be {@code null}.
+ * @param value The data to associate with the key, may be {@code null} to remove the mapping.
+ */
+ void setData( Object key, Object value );
+
+ /**
+ * Traverses this node and potentially its children using the specified visitor.
+ *
+ * @param visitor The visitor to call back, must not be {@code null}.
+ * @return {@code true} to visit siblings nodes of this node as well, {@code false} to skip siblings.
+ */
+ boolean accept( DependencyVisitor visitor );
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyVisitor.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyVisitor.java b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyVisitor.java
new file mode 100644
index 0000000..2a85f2d
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyVisitor.java
@@ -0,0 +1,47 @@
+package org.eclipse.aether.graph;
+
+/*
+ * 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 visitor for nodes of the dependency graph.
+ *
+ * @see DependencyNode#accept(DependencyVisitor)
+ */
+public interface DependencyVisitor
+{
+
+ /**
+ * Notifies the visitor of a node visit before its children have been processed.
+ *
+ * @param node The dependency node being visited, must not be {@code null}.
+ * @return {@code true} to visit child nodes of the specified node as well, {@code false} to skip children.
+ */
+ boolean visitEnter( DependencyNode node );
+
+ /**
+ * Notifies the visitor of a node visit after its children have been processed. Note that this method is always
+ * invoked regardless whether any children have actually been visited.
+ *
+ * @param node The dependency node being visited, must not be {@code null}.
+ * @return {@code true} to visit siblings nodes of the specified node as well, {@code false} to skip siblings.
+ */
+ boolean visitLeave( DependencyNode node );
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/graph/Exclusion.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/Exclusion.java b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/Exclusion.java
new file mode 100644
index 0000000..497cf43
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/Exclusion.java
@@ -0,0 +1,131 @@
+package org.eclipse.aether.graph;
+
+/*
+ * 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.
+ */
+
+/**
+ * An exclusion of one or more transitive dependencies. <em>Note:</em> Instances of this class are immutable and the
+ * exposed mutators return new objects rather than changing the current instance.
+ *
+ * @see Dependency#getExclusions()
+ */
+public final class Exclusion
+{
+
+ private final String groupId;
+
+ private final String artifactId;
+
+ private final String classifier;
+
+ private final String extension;
+
+ /**
+ * Creates an exclusion for artifacts with the specified coordinates.
+ *
+ * @param groupId The group identifier, may be {@code null}.
+ * @param artifactId The artifact identifier, may be {@code null}.
+ * @param classifier The classifier, may be {@code null}.
+ * @param extension The file extension, may be {@code null}.
+ */
+ public Exclusion( String groupId, String artifactId, String classifier, String extension )
+ {
+ this.groupId = ( groupId != null ) ? groupId : "";
+ this.artifactId = ( artifactId != null ) ? artifactId : "";
+ this.classifier = ( classifier != null ) ? classifier : "";
+ this.extension = ( extension != null ) ? extension : "";
+ }
+
+ /**
+ * Gets the group identifier for artifacts to exclude.
+ *
+ * @return The group identifier, never {@code null}.
+ */
+ public String getGroupId()
+ {
+ return groupId;
+ }
+
+ /**
+ * Gets the artifact identifier for artifacts to exclude.
+ *
+ * @return The artifact identifier, never {@code null}.
+ */
+ public String getArtifactId()
+ {
+ return artifactId;
+ }
+
+ /**
+ * Gets the classifier for artifacts to exclude.
+ *
+ * @return The classifier, never {@code null}.
+ */
+ public String getClassifier()
+ {
+ return classifier;
+ }
+
+ /**
+ * Gets the file extension for artifacts to exclude.
+ *
+ * @return The file extension of artifacts to exclude, never {@code null}.
+ */
+ public String getExtension()
+ {
+ return extension;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getGroupId() + ':' + getArtifactId() + ':' + getExtension()
+ + ( getClassifier().length() > 0 ? ':' + getClassifier() : "" );
+ }
+
+ @Override
+ public boolean equals( Object obj )
+ {
+ if ( obj == this )
+ {
+ return true;
+ }
+ else if ( obj == null || !getClass().equals( obj.getClass() ) )
+ {
+ return false;
+ }
+
+ Exclusion that = (Exclusion) obj;
+
+ return artifactId.equals( that.artifactId ) && groupId.equals( that.groupId )
+ && extension.equals( that.extension ) && classifier.equals( that.classifier );
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 17;
+ hash = hash * 31 + artifactId.hashCode();
+ hash = hash * 31 + groupId.hashCode();
+ hash = hash * 31 + classifier.hashCode();
+ hash = hash * 31 + extension.hashCode();
+ return hash;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/graph/package-info.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/package-info.java b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/package-info.java
new file mode 100644
index 0000000..c3ba9db
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/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 representation of a dependency graph by means of connected dependency nodes.
+ */
+package org.eclipse.aether.graph;
+
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallRequest.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallRequest.java b/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallRequest.java
new file mode 100644
index 0000000..f9b3163
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallRequest.java
@@ -0,0 +1,177 @@
+package org.eclipse.aether.installation;
+
+/*
+ * 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 org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.RequestTrace;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.metadata.Metadata;
+
+/**
+ * A request to install artifacts and their accompanying metadata into the local repository.
+ *
+ * @see RepositorySystem#install(RepositorySystemSession, InstallRequest)
+ */
+public final class InstallRequest
+{
+
+ private Collection<Artifact> artifacts = Collections.emptyList();
+
+ private Collection<Metadata> metadata = Collections.emptyList();
+
+ private RequestTrace trace;
+
+ /**
+ * Creates an uninitialized request.
+ */
+ public InstallRequest()
+ {
+ }
+
+ /**
+ * Gets the artifact to install.
+ *
+ * @return The artifacts to install, never {@code null}.
+ */
+ public Collection<Artifact> getArtifacts()
+ {
+ return artifacts;
+ }
+
+ /**
+ * Sets the artifacts to install.
+ *
+ * @param artifacts The artifacts to install, may be {@code null}.
+ * @return This request for chaining, never {@code null}.
+ */
+ public InstallRequest setArtifacts( Collection<Artifact> artifacts )
+ {
+ if ( artifacts == null )
+ {
+ this.artifacts = Collections.emptyList();
+ }
+ else
+ {
+ this.artifacts = artifacts;
+ }
+ return this;
+ }
+
+ /**
+ * Adds the specified artifacts for installation.
+ *
+ * @param artifact The artifact to add, may be {@code null}.
+ * @return This request for chaining, never {@code null}.
+ */
+ public InstallRequest addArtifact( Artifact artifact )
+ {
+ if ( artifact != null )
+ {
+ if ( artifacts.isEmpty() )
+ {
+ artifacts = new ArrayList<Artifact>();
+ }
+ artifacts.add( artifact );
+ }
+ return this;
+ }
+
+ /**
+ * Gets the metadata to install.
+ *
+ * @return The metadata to install, never {@code null}.
+ */
+ public Collection<Metadata> getMetadata()
+ {
+ return metadata;
+ }
+
+ /**
+ * Sets the metadata to install.
+ *
+ * @param metadata The metadata to install.
+ * @return This request for chaining, never {@code null}.
+ */
+ public InstallRequest setMetadata( Collection<Metadata> metadata )
+ {
+ if ( metadata == null )
+ {
+ this.metadata = Collections.emptyList();
+ }
+ else
+ {
+ this.metadata = metadata;
+ }
+ return this;
+ }
+
+ /**
+ * Adds the specified metadata for installation.
+ *
+ * @param metadata The metadata to add, may be {@code null}.
+ * @return This request for chaining, never {@code null}.
+ */
+ public InstallRequest addMetadata( Metadata metadata )
+ {
+ if ( metadata != null )
+ {
+ if ( this.metadata.isEmpty() )
+ {
+ this.metadata = new ArrayList<Metadata>();
+ }
+ this.metadata.add( metadata );
+ }
+ 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 InstallRequest setTrace( RequestTrace trace )
+ {
+ this.trace = trace;
+ return this;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getArtifacts() + ", " + getMetadata();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallResult.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallResult.java b/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallResult.java
new file mode 100644
index 0000000..6624585
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallResult.java
@@ -0,0 +1,174 @@
+package org.eclipse.aether.installation;
+
+/*
+ * 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 org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.metadata.Metadata;
+
+/**
+ * The result of installing artifacts and their accompanying metadata into the a remote repository.
+ *
+ * @see RepositorySystem#install(RepositorySystemSession, InstallRequest)
+ */
+public final class InstallResult
+{
+
+ private final InstallRequest request;
+
+ private Collection<Artifact> artifacts;
+
+ private Collection<Metadata> metadata;
+
+ /**
+ * Creates a new result for the specified request.
+ *
+ * @param request The installation request, must not be {@code null}.
+ */
+ public InstallResult( InstallRequest request )
+ {
+ if ( request == null )
+ {
+ throw new IllegalArgumentException( "install request has not been specified" );
+ }
+ this.request = request;
+ artifacts = Collections.emptyList();
+ metadata = Collections.emptyList();
+ }
+
+ /**
+ * Gets the install request that was made.
+ *
+ * @return The install request, never {@code null}.
+ */
+ public InstallRequest getRequest()
+ {
+ return request;
+ }
+
+ /**
+ * Gets the artifacts that got installed.
+ *
+ * @return The installed artifacts, never {@code null}.
+ */
+ public Collection<Artifact> getArtifacts()
+ {
+ return artifacts;
+ }
+
+ /**
+ * Sets the artifacts that got installed.
+ *
+ * @param artifacts The installed artifacts, may be {@code null}.
+ * @return This result for chaining, never {@code null}.
+ */
+ public InstallResult setArtifacts( Collection<Artifact> artifacts )
+ {
+ if ( artifacts == null )
+ {
+ this.artifacts = Collections.emptyList();
+ }
+ else
+ {
+ this.artifacts = artifacts;
+ }
+ return this;
+ }
+
+ /**
+ * Adds the specified artifacts to the result.
+ *
+ * @param artifact The installed artifact to add, may be {@code null}.
+ * @return This result for chaining, never {@code null}.
+ */
+ public InstallResult addArtifact( Artifact artifact )
+ {
+ if ( artifact != null )
+ {
+ if ( artifacts.isEmpty() )
+ {
+ artifacts = new ArrayList<Artifact>();
+ }
+ artifacts.add( artifact );
+ }
+ return this;
+ }
+
+ /**
+ * Gets the metadata that got installed. Note that due to automatically generated metadata, there might have been
+ * more metadata installed than originally specified in the install request.
+ *
+ * @return The installed metadata, never {@code null}.
+ */
+ public Collection<Metadata> getMetadata()
+ {
+ return metadata;
+ }
+
+ /**
+ * Sets the metadata that got installed.
+ *
+ * @param metadata The installed metadata, may be {@code null}.
+ * @return This result for chaining, never {@code null}.
+ */
+ public InstallResult setMetadata( Collection<Metadata> metadata )
+ {
+ if ( metadata == null )
+ {
+ this.metadata = Collections.emptyList();
+ }
+ else
+ {
+ this.metadata = metadata;
+ }
+ return this;
+ }
+
+ /**
+ * Adds the specified metadata to this result.
+ *
+ * @param metadata The installed metadata to add, may be {@code null}.
+ * @return This result for chaining, never {@code null}.
+ */
+ public InstallResult addMetadata( Metadata metadata )
+ {
+ if ( metadata != null )
+ {
+ if ( this.metadata.isEmpty() )
+ {
+ this.metadata = new ArrayList<Metadata>();
+ }
+ this.metadata.add( metadata );
+ }
+ return this;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getArtifacts() + ", " + getMetadata();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallationException.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallationException.java b/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallationException.java
new file mode 100644
index 0000000..9a556bb
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/installation/InstallationException.java
@@ -0,0 +1,52 @@
+package org.eclipse.aether.installation;
+
+/*
+ * 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 an installation error like an IO error.
+ */
+public class InstallationException
+ extends RepositoryException
+{
+
+ /**
+ * Creates a new exception with the specified detail message.
+ *
+ * @param message The detail message, may be {@code null}.
+ */
+ public InstallationException( String message )
+ {
+ super( message );
+ }
+
+ /**
+ * Creates a new exception with the specified detail message and cause.
+ *
+ * @param message The detail message, may be {@code null}.
+ * @param cause The exception that caused this one, may be {@code null}.
+ */
+ public InstallationException( String message, Throwable cause )
+ {
+ super( message, cause );
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/installation/package-info.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/installation/package-info.java b/maven-resolver-api/src/main/java/org/eclipse/aether/installation/package-info.java
new file mode 100644
index 0000000..d4ac077
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/installation/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 types supporting the publishing of artifacts to a local repository.
+ */
+package org.eclipse.aether.installation;
+
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/AbstractMetadata.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/AbstractMetadata.java b/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/AbstractMetadata.java
new file mode 100644
index 0000000..49dab35
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/AbstractMetadata.java
@@ -0,0 +1,160 @@
+package org.eclipse.aether.metadata;
+
+/*
+ * 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;
+
+/**
+ * A skeleton class for metadata.
+ */
+public abstract class AbstractMetadata
+ implements Metadata
+{
+
+ private Metadata newInstance( Map<String, String> properties, File file )
+ {
+ return new DefaultMetadata( getGroupId(), getArtifactId(), getVersion(), getType(), getNature(), file,
+ properties );
+ }
+
+ public Metadata setFile( File file )
+ {
+ File current = getFile();
+ if ( ( current == null ) ? file == null : current.equals( file ) )
+ {
+ return this;
+ }
+ return newInstance( getProperties(), file );
+ }
+
+ public Metadata setProperties( Map<String, String> properties )
+ {
+ Map<String, String> current = getProperties();
+ if ( current.equals( properties ) || ( properties == null && current.isEmpty() ) )
+ {
+ return this;
+ }
+ return newInstance( copyProperties( properties ), getFile() );
+ }
+
+ public String getProperty( String key, String defaultValue )
+ {
+ String value = getProperties().get( key );
+ return ( value != null ) ? value : defaultValue;
+ }
+
+ /**
+ * Copies the specified metadata properties. This utility method should be used when creating new metadata instances
+ * with caller-supplied properties.
+ *
+ * @param properties The properties to copy, may be {@code null}.
+ * @return The copied and read-only properties, never {@code null}.
+ */
+ protected static Map<String, String> copyProperties( Map<String, String> properties )
+ {
+ if ( properties != null && !properties.isEmpty() )
+ {
+ return Collections.unmodifiableMap( new HashMap<String, String>( properties ) );
+ }
+ else
+ {
+ return Collections.emptyMap();
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder buffer = new StringBuilder( 128 );
+ if ( getGroupId().length() > 0 )
+ {
+ buffer.append( getGroupId() );
+ }
+ if ( getArtifactId().length() > 0 )
+ {
+ buffer.append( ':' ).append( getArtifactId() );
+ }
+ if ( getVersion().length() > 0 )
+ {
+ buffer.append( ':' ).append( getVersion() );
+ }
+ buffer.append( '/' ).append( getType() );
+ return buffer.toString();
+ }
+
+ /**
+ * Compares this metadata with the specified object.
+ *
+ * @param obj The object to compare this metadata against, may be {@code null}.
+ * @return {@code true} if and only if the specified object is another {@link Metadata} with equal coordinates,
+ * type, nature, properties and file, {@code false} otherwise.
+ */
+ @Override
+ public boolean equals( Object obj )
+ {
+ if ( obj == this )
+ {
+ return true;
+ }
+ else if ( !( obj instanceof Metadata ) )
+ {
+ return false;
+ }
+
+ Metadata that = (Metadata) obj;
+
+ return getArtifactId().equals( that.getArtifactId() ) && getGroupId().equals( that.getGroupId() )
+ && getVersion().equals( that.getVersion() ) && getType().equals( that.getType() )
+ && getNature().equals( that.getNature() ) && eq( getFile(), that.getFile() )
+ && eq( getProperties(), that.getProperties() );
+ }
+
+ private static <T> boolean eq( T s1, T s2 )
+ {
+ return s1 != null ? s1.equals( s2 ) : s2 == null;
+ }
+
+ /**
+ * Returns a hash code for this metadata.
+ *
+ * @return A hash code for the metadata.
+ */
+ @Override
+ public int hashCode()
+ {
+ int hash = 17;
+ hash = hash * 31 + getGroupId().hashCode();
+ hash = hash * 31 + getArtifactId().hashCode();
+ hash = hash * 31 + getType().hashCode();
+ hash = hash * 31 + getNature().hashCode();
+ hash = hash * 31 + getVersion().hashCode();
+ hash = hash * 31 + hash( getFile() );
+ return hash;
+ }
+
+ private static int hash( Object obj )
+ {
+ return ( obj != null ) ? obj.hashCode() : 0;
+ }
+
+}