001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.collection;
020
021import java.util.Collection;
022import java.util.Collections;
023import java.util.Iterator;
024import java.util.LinkedHashSet;
025import java.util.List;
026
027import org.eclipse.aether.RepositoryException;
028import org.eclipse.aether.artifact.Artifact;
029import org.eclipse.aether.graph.DependencyNode;
030import org.eclipse.aether.version.VersionConstraint;
031
032/**
033 * Thrown in case of an unsolvable conflict between different version constraints for a dependency.
034 */
035public class UnsolvableVersionConflictException extends RepositoryException {
036
037    private final transient Collection<String> versions;
038
039    private final transient Collection<? extends List<? extends DependencyNode>> paths;
040
041    /**
042     * Creates a new exception with the specified paths to conflicting nodes in the dependency graph.
043     *
044     * @param paths The paths to the dependency nodes that participate in the version conflict, may be {@code null}.
045     */
046    public UnsolvableVersionConflictException(Collection<? extends List<? extends DependencyNode>> paths) {
047        super("Could not resolve version conflict among " + toPaths(paths));
048        if (paths == null) {
049            this.paths = Collections.emptyList();
050            this.versions = Collections.emptyList();
051        } else {
052            this.paths = paths;
053            this.versions = new LinkedHashSet<>();
054            for (List<? extends DependencyNode> path : paths) {
055                VersionConstraint constraint = path.get(path.size() - 1).getVersionConstraint();
056                if (constraint != null && constraint.getRange() != null) {
057                    versions.add(constraint.toString());
058                }
059            }
060        }
061    }
062
063    private static String toPaths(Collection<? extends List<? extends DependencyNode>> paths) {
064        String result = "";
065
066        if (paths != null) {
067            Collection<String> strings = new LinkedHashSet<>();
068
069            for (List<? extends DependencyNode> path : paths) {
070                strings.add(toPath(path));
071            }
072
073            result = strings.toString();
074        }
075
076        return result;
077    }
078
079    private static String toPath(List<? extends DependencyNode> path) {
080        StringBuilder buffer = new StringBuilder(256);
081
082        for (Iterator<? extends DependencyNode> it = path.iterator(); it.hasNext(); ) {
083            DependencyNode node = it.next();
084            if (node.getDependency() == null) {
085                continue;
086            }
087
088            Artifact artifact = node.getDependency().getArtifact();
089            buffer.append(artifact.getGroupId());
090            buffer.append(':').append(artifact.getArtifactId());
091            buffer.append(':').append(artifact.getExtension());
092            if (!artifact.getClassifier().isEmpty()) {
093                buffer.append(':').append(artifact.getClassifier());
094            }
095            buffer.append(':').append(node.getVersionConstraint());
096
097            if (it.hasNext()) {
098                buffer.append(" -> ");
099            }
100        }
101
102        return buffer.toString();
103    }
104
105    /**
106     * Gets the paths leading to the conflicting dependencies.
107     *
108     * @return The (read-only) paths leading to the conflicting dependencies, never {@code null}.
109     */
110    public Collection<? extends List<? extends DependencyNode>> getPaths() {
111        return paths;
112    }
113
114    /**
115     * Gets the conflicting version constraints of the dependency.
116     *
117     * @return The (read-only) conflicting version constraints, never {@code null}.
118     */
119    public Collection<String> getVersions() {
120        return versions;
121    }
122}