1 package org.eclipse.aether.util.graph.transformer;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashSet;
25 import java.util.Iterator;
26
27 import org.eclipse.aether.RepositoryException;
28 import org.eclipse.aether.collection.UnsolvableVersionConflictException;
29 import org.eclipse.aether.graph.DependencyFilter;
30 import org.eclipse.aether.graph.DependencyNode;
31 import org.eclipse.aether.util.graph.transformer.ConflictResolver.ConflictContext;
32 import org.eclipse.aether.util.graph.transformer.ConflictResolver.ConflictItem;
33 import org.eclipse.aether.util.graph.transformer.ConflictResolver.VersionSelector;
34 import org.eclipse.aether.util.graph.visitor.PathRecordingDependencyVisitor;
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 public final class NearestVersionSelector
45 extends VersionSelector
46 {
47
48
49
50
51 public NearestVersionSelector()
52 {
53 }
54
55 @Override
56 public void selectVersion( ConflictContext context )
57 throws RepositoryException
58 {
59 ConflictGroup group = new ConflictGroup();
60 for ( ConflictItem item : context.getItems() )
61 {
62 DependencyNode node = item.getNode();
63 VersionConstraint constraint = node.getVersionConstraint();
64
65 boolean backtrack = false;
66 boolean hardConstraint = constraint.getRange() != null;
67
68 if ( hardConstraint )
69 {
70 if ( group.constraints.add( constraint ) )
71 {
72 if ( group.winner != null && !constraint.containsVersion( group.winner.getNode().getVersion() ) )
73 {
74 backtrack = true;
75 }
76 }
77 }
78
79 if ( isAcceptable( group, node.getVersion() ) )
80 {
81 group.candidates.add( item );
82
83 if ( backtrack )
84 {
85 backtrack( group, context );
86 }
87 else if ( group.winner == null || isNearer( item, group.winner ) )
88 {
89 group.winner = item;
90 }
91 }
92 else if ( backtrack )
93 {
94 backtrack( group, context );
95 }
96 }
97 context.setWinner( group.winner );
98 }
99
100 private void backtrack( ConflictGroup group, ConflictContext context )
101 throws UnsolvableVersionConflictException
102 {
103 group.winner = null;
104
105 for ( Iterator<ConflictItem> it = group.candidates.iterator(); it.hasNext(); )
106 {
107 ConflictItem candidate = it.next();
108
109 if ( !isAcceptable( group, candidate.getNode().getVersion() ) )
110 {
111 it.remove();
112 }
113 else if ( group.winner == null || isNearer( candidate, group.winner ) )
114 {
115 group.winner = candidate;
116 }
117 }
118
119 if ( group.winner == null )
120 {
121 throw newFailure( context );
122 }
123 }
124
125 private boolean isAcceptable( ConflictGroup group, Version version )
126 {
127 for ( VersionConstraint constraint : group.constraints )
128 {
129 if ( !constraint.containsVersion( version ) )
130 {
131 return false;
132 }
133 }
134 return true;
135 }
136
137 private boolean isNearer( ConflictItem item1, ConflictItem item2 )
138 {
139 if ( item1.isSibling( item2 ) )
140 {
141 return item1.getNode().getVersion().compareTo( item2.getNode().getVersion() ) > 0;
142 }
143 else
144 {
145 return item1.getDepth() < item2.getDepth();
146 }
147 }
148
149 private UnsolvableVersionConflictException newFailure( final ConflictContext context )
150 {
151 DependencyFilter filter = ( node, parents ) ->
152 {
153 requireNonNull( node, "node cannot be null" );
154 requireNonNull( parents, "parents cannot be null" );
155 return context.isIncluded( node );
156 };
157 PathRecordingDependencyVisitor visitor = new PathRecordingDependencyVisitor( filter );
158 context.getRoot().accept( visitor );
159 return new UnsolvableVersionConflictException( visitor.getPaths() );
160 }
161
162 static final class ConflictGroup
163 {
164
165 final Collection<VersionConstraint> constraints;
166
167 final Collection<ConflictItem> candidates;
168
169 ConflictItem winner;
170
171 ConflictGroup()
172 {
173 constraints = new HashSet<>();
174 candidates = new ArrayList<>( 64 );
175 }
176
177 @Override
178 public String toString()
179 {
180 return String.valueOf( winner );
181 }
182
183 }
184
185 }