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.util.graph.transformer;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  
26  import org.eclipse.aether.RepositoryException;
27  import org.eclipse.aether.collection.UnsolvableVersionConflictException;
28  import org.eclipse.aether.graph.DependencyFilter;
29  import org.eclipse.aether.graph.DependencyNode;
30  import org.eclipse.aether.util.graph.transformer.ConflictResolver.ConflictContext;
31  import org.eclipse.aether.util.graph.transformer.ConflictResolver.ConflictItem;
32  import org.eclipse.aether.util.graph.transformer.ConflictResolver.VersionSelector;
33  import org.eclipse.aether.util.graph.visitor.PathRecordingDependencyVisitor;
34  import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor;
35  import org.eclipse.aether.version.Version;
36  import org.eclipse.aether.version.VersionConstraint;
37  
38  import static java.util.Objects.requireNonNull;
39  
40  /**
41   * A version selector for use with {@link ConflictResolver} that resolves version conflicts using a nearest-wins
42   * strategy. If there is no single node that satisfies all encountered version ranges, the selector will fail.
43   *
44   * @deprecated Use {@link ConfigurableVersionSelector} instead.
45   */
46  @Deprecated
47  public final class NearestVersionSelector extends VersionSelector {
48  
49      /**
50       * Creates a new instance of this version selector.
51       */
52      public NearestVersionSelector() {}
53  
54      @Override
55      public void selectVersion(ConflictContext context) throws RepositoryException {
56          ConflictGroup group = new ConflictGroup();
57          for (ConflictItem item : context.getItems()) {
58              DependencyNode node = item.getNode();
59              VersionConstraint constraint = node.getVersionConstraint();
60  
61              boolean backtrack = false;
62              boolean hardConstraint = constraint.getRange() != null;
63  
64              if (hardConstraint) {
65                  if (group.constraints.add(constraint)) {
66                      if (group.winner != null
67                              && !constraint.containsVersion(
68                                      group.winner.getNode().getVersion())) {
69                          backtrack = true;
70                      }
71                  }
72              }
73  
74              if (isAcceptable(group, node.getVersion())) {
75                  group.candidates.add(item);
76  
77                  if (backtrack) {
78                      backtrack(group, context);
79                  } else if (group.winner == null || isNearer(item, group.winner)) {
80                      group.winner = item;
81                  }
82              } else if (backtrack) {
83                  backtrack(group, context);
84              }
85          }
86          context.setWinner(group.winner);
87      }
88  
89      private void backtrack(ConflictGroup group, ConflictContext context) throws UnsolvableVersionConflictException {
90          group.winner = null;
91  
92          for (Iterator<ConflictItem> it = group.candidates.iterator(); it.hasNext(); ) {
93              ConflictItem candidate = it.next();
94  
95              if (!isAcceptable(group, candidate.getNode().getVersion())) {
96                  it.remove();
97              } else if (group.winner == null || isNearer(candidate, group.winner)) {
98                  group.winner = candidate;
99              }
100         }
101 
102         if (group.winner == null) {
103             throw newFailure(context);
104         }
105     }
106 
107     private boolean isAcceptable(ConflictGroup group, Version version) {
108         for (VersionConstraint constraint : group.constraints) {
109             if (!constraint.containsVersion(version)) {
110                 return false;
111             }
112         }
113         return true;
114     }
115 
116     private boolean isNearer(ConflictItem item1, ConflictItem item2) {
117         if (item1.isSibling(item2)) {
118             return item1.getNode().getVersion().compareTo(item2.getNode().getVersion()) > 0;
119         } else {
120             return item1.getDepth() < item2.getDepth();
121         }
122     }
123 
124     private UnsolvableVersionConflictException newFailure(final ConflictContext context) {
125         DependencyFilter filter = (node, parents) -> {
126             requireNonNull(node, "node cannot be null");
127             requireNonNull(parents, "parents cannot be null");
128             return context.isIncluded(node);
129         };
130         PathRecordingDependencyVisitor visitor = new PathRecordingDependencyVisitor(filter);
131         context.getRoot().accept(new TreeDependencyVisitor(visitor));
132         return new UnsolvableVersionConflictException(visitor.getPaths());
133     }
134 
135     static final class ConflictGroup {
136 
137         final Collection<VersionConstraint> constraints;
138 
139         final Collection<ConflictItem> candidates;
140 
141         ConflictItem winner;
142 
143         ConflictGroup() {
144             constraints = new HashSet<>();
145             candidates = new ArrayList<>(64);
146         }
147 
148         @Override
149         public String toString() {
150             return String.valueOf(winner);
151         }
152     }
153 }