1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
42
43
44
45
46 @Deprecated
47 public final class NearestVersionSelector extends VersionSelector {
48
49
50
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 }