/*
 * Copyright 2015 Lukas Krejci
 *
 * Licensed 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
 */

package org.revapi.maven.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import javax.annotation.Nullable;

import org.eclipse.aether.RepositoryException;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.graph.DependencyVisitor;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.resolution.DependencyResult;
import org.eclipse.aether.resolution.VersionRangeRequest;
import org.eclipse.aether.resolution.VersionRangeResolutionException;
import org.eclipse.aether.resolution.VersionRangeResult;
import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor;
import org.eclipse.aether.version.Version;

/**
 * @author Lukas Krejci
 * @since 0.3.0
 */
public class ArtifactResolver {
    private final RepositorySystem repositorySystem;
    private final RepositorySystemSession session;
    private final List<RemoteRepository> repositories;

    public ArtifactResolver(RepositorySystem repositorySystem, RepositorySystemSession session,
        List<RemoteRepository> repositories) {
        this.repositorySystem = repositorySystem;
        this.session = session;
        this.repositories = repositories;
    }


    public Artifact resolveArtifact(String gav) throws ArtifactResolutionException {
        return resolveArtifact(new DefaultArtifact(gav));
    }

    /**
     * Tries to find the newest version of the artifact that matches given regular expression.
     * The found version will be older than the {@code upToVersion} or newest available if {@code upToVersion} is null.
     *
     * @param gav the coordinates of the artifact. The version part is ignored
     * @param upToVersion the version up to which the versions will be matched
     * @param versionMatcher the matcher to match the version
     * @return
     * @throws VersionRangeResolutionException
     */
    public Artifact resolveNewestMatching(String gav, @Nullable String upToVersion, Pattern versionMatcher)
            throws VersionRangeResolutionException, ArtifactResolutionException {
        Artifact artifact = new DefaultArtifact(gav);
        artifact = artifact.setVersion(upToVersion == null ? "[,)" : "[," + upToVersion + ")");
        VersionRangeRequest rangeRequest = new VersionRangeRequest(artifact, null, null);

        VersionRangeResult result = repositorySystem.resolveVersionRange(session, rangeRequest);

        List<Version> versions = new ArrayList<>(result.getVersions());
        Collections.reverse(versions);

        for(Version v : versions) {
            if (versionMatcher.matcher(v.toString()).matches()) {
                return resolveArtifact(artifact.setVersion(v.toString()));
            }
        }


        throw new VersionRangeResolutionException(result) {
            @Override
            public String getMessage() {
                return "Failed to find a version of artifact '" + gav + "' that would correspond to an expression '"
                        + versionMatcher + "'. The versions found were: " + versions;
            }
        };
    }

    public CollectionResult collectTransitiveDeps(String... gavs) throws RepositoryException {

        Set<Artifact> artifacts = new HashSet<>();
        Set<Exception> failures = new HashSet<>();

        for (String gav : gavs) {
            collectTransitiveDeps(gav, artifacts, failures);
        }

        return new CollectionResult(failures, artifacts);
    }

    protected void collectTransitiveDeps(String gav, Set<Artifact> resolvedArtifacts, Set<Exception> failures)
            throws RepositoryException {

        final Artifact rootArtifact = resolveArtifact(gav);

        CollectRequest collectRequest = new CollectRequest(new Dependency(rootArtifact, null), repositories);

        DependencyRequest request = new DependencyRequest(collectRequest, null);

        DependencyResult result;

        try {
            result = repositorySystem.resolveDependencies(session, request);
        } catch (DependencyResolutionException dre) {
            result = dre.getResult();
        }

        result.getRoot().accept(new TreeDependencyVisitor(new DependencyVisitor() {
            @Override
            public boolean visitEnter(DependencyNode node) {
                return true;
            }

            @Override
            public boolean visitLeave(DependencyNode node) {
                Dependency dep = node.getDependency();
                if (dep == null || dep.getArtifact().equals(rootArtifact)) {
                    return true;
                }

                resolvedArtifacts.add(dep.getArtifact());

                return true;
            }
        }));

        failures.addAll(result.getCollectExceptions());
    }

    public static final class CollectionResult {
        private final Set<Artifact> resolvedArtifacts;
        private final Set<Exception> failures;

        private CollectionResult(Set<Exception> failures, Set<Artifact> resolvedArtifacts) {
            this.failures = failures;
            this.resolvedArtifacts = resolvedArtifacts;
        }

        public Set<Exception> getFailures() {
            return failures;
        }

        public Set<Artifact> getResolvedArtifacts() {
            return resolvedArtifacts;
        }
    }

    private Artifact resolveArtifact(Artifact artifact) throws ArtifactResolutionException {
        ArtifactRequest request = new ArtifactRequest().setArtifact(artifact)
                .setRepositories(repositories);

        ArtifactResult result = repositorySystem.resolveArtifact(session, request);
        return result.getArtifact();
    }
}

@@ -3561,20 +3561,28 @@
 tifact, 
-null
+repositories
 , null);

Make sure to perform the version range request against all the repos when figuring out the latest matching versions.