View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.collection;
20  
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.Iterator;
24  import java.util.LinkedHashSet;
25  import java.util.List;
26  
27  import org.eclipse.aether.RepositoryException;
28  import org.eclipse.aether.artifact.Artifact;
29  import org.eclipse.aether.graph.DependencyNode;
30  import org.eclipse.aether.version.VersionConstraint;
31  
32  import static java.util.Objects.requireNonNull;
33  
34  /**
35   * Thrown in case of an unsolvable conflict between different version constraints for a dependency.
36   */
37  public class UnsolvableVersionConflictException extends RepositoryException {
38  
39      private final transient Collection<String> versions;
40  
41      private final transient Collection<? extends List<? extends DependencyNode>> paths;
42  
43      /**
44       * Creates a new exception with the specified paths to conflicting nodes in the dependency graph.
45       *
46       * @param paths The paths to the dependency nodes that participate in the version conflict, may be {@code null}.
47       * @deprecated Use {@link #UnsolvableVersionConflictException(String, Collection)} instead.
48       */
49      @Deprecated
50      public UnsolvableVersionConflictException(Collection<? extends List<? extends DependencyNode>> paths) {
51          this("Unsolvable hard constraint combination", paths);
52      }
53  
54      /**
55       * Creates a new exception with the specified paths to conflicting nodes in the dependency graph.
56       *
57       * @param message The strategy that throw the bucket in, must not be {@code null}. Should provide concise message
58       *                why this exception was thrown.
59       * @param paths The paths to the dependency nodes that participate in the version conflict, may be {@code null}.
60       *
61       * @since 2.0.0
62       */
63      public UnsolvableVersionConflictException(
64              String message, Collection<? extends List<? extends DependencyNode>> paths) {
65          super(requireNonNull(message, "message") + "; Could not resolve version conflict among " + toPaths(paths));
66          if (paths == null) {
67              this.paths = Collections.emptyList();
68              this.versions = Collections.emptyList();
69          } else {
70              this.paths = paths;
71              this.versions = new LinkedHashSet<>();
72              for (List<? extends DependencyNode> path : paths) {
73                  VersionConstraint constraint = path.get(path.size() - 1).getVersionConstraint();
74                  if (constraint != null && constraint.getRange() != null) {
75                      versions.add(constraint.toString());
76                  }
77              }
78          }
79      }
80  
81      private static String toPaths(Collection<? extends List<? extends DependencyNode>> paths) {
82          String result = "";
83  
84          if (paths != null) {
85              Collection<String> strings = new LinkedHashSet<>();
86  
87              for (List<? extends DependencyNode> path : paths) {
88                  strings.add(toPath(path));
89              }
90  
91              result = strings.toString();
92          }
93  
94          return result;
95      }
96  
97      private static String toPath(List<? extends DependencyNode> path) {
98          StringBuilder buffer = new StringBuilder(256);
99  
100         for (Iterator<? extends DependencyNode> it = path.iterator(); it.hasNext(); ) {
101             DependencyNode node = it.next();
102             if (node.getDependency() == null) {
103                 continue;
104             }
105 
106             Artifact artifact = node.getDependency().getArtifact();
107             buffer.append(artifact.getGroupId());
108             buffer.append(':').append(artifact.getArtifactId());
109             buffer.append(':').append(artifact.getExtension());
110             if (!artifact.getClassifier().isEmpty()) {
111                 buffer.append(':').append(artifact.getClassifier());
112             }
113             buffer.append(':').append(node.getVersionConstraint());
114 
115             if (it.hasNext()) {
116                 buffer.append(" -> ");
117             }
118         }
119 
120         return buffer.toString();
121     }
122 
123     /**
124      * Gets the paths leading to the conflicting dependencies.
125      *
126      * @return The (read-only) paths leading to the conflicting dependencies, never {@code null}.
127      */
128     public Collection<? extends List<? extends DependencyNode>> getPaths() {
129         return paths;
130     }
131 
132     /**
133      * Gets the conflicting version constraints of the dependency.
134      *
135      * @return The (read-only) conflicting version constraints, never {@code null}.
136      */
137     public Collection<String> getVersions() {
138         return versions;
139     }
140 }