1 package org.eclipse.aether.internal.impl.collect.bf;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.eclipse.aether.artifact.Artifact;
23 import org.eclipse.aether.graph.DependencyNode;
24 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 import java.util.HashMap;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.concurrent.atomic.AtomicInteger;
33
34
35
36
37
38
39
40 abstract class DependencyResolutionSkipper
41 {
42
43
44
45
46
47
48
49
50 abstract boolean skipResolution( DependencyNode node, List<DependencyNode> parents );
51
52
53
54
55
56
57
58 abstract void cache( DependencyNode node, List<DependencyNode> parents );
59
60
61
62
63 abstract void report();
64
65
66
67
68
69
70 public static DefaultDependencyResolutionSkipper defaultSkipper()
71 {
72 return new DefaultDependencyResolutionSkipper();
73 }
74
75
76
77
78 public static DependencyResolutionSkipper neverSkipper()
79 {
80 return NeverDependencyResolutionSkipper.INSTANCE;
81 }
82
83
84
85
86 private static final class NeverDependencyResolutionSkipper extends DependencyResolutionSkipper
87 {
88 private static final DependencyResolutionSkipper INSTANCE = new NeverDependencyResolutionSkipper();
89
90 @Override
91 public boolean skipResolution( DependencyNode node, List<DependencyNode> parents )
92 {
93 return false;
94 }
95
96 @Override
97 public void cache( DependencyNode node, List<DependencyNode> parents )
98 {
99 }
100
101 @Override
102 public void report()
103 {
104 }
105 }
106
107
108
109
110 static final class DefaultDependencyResolutionSkipper extends DependencyResolutionSkipper
111 {
112 private static final Logger LOGGER = LoggerFactory.getLogger( DependencyResolutionSkipper.class );
113
114 private final Map<DependencyNode, DependencyResolutionResult> results = new LinkedHashMap<>( 256 );
115 private final CacheManager cacheManager = new CacheManager();
116 private final CoordinateManager coordinateManager = new CoordinateManager();
117
118 @Override
119 public boolean skipResolution( DependencyNode node, List<DependencyNode> parents )
120 {
121 DependencyResolutionResult result = new DependencyResolutionResult( node );
122 results.put( node, result );
123
124 int depth = parents.size() + 1;
125 coordinateManager.createCoordinate( node, depth );
126
127 if ( cacheManager.isVersionConflict( node ) )
128 {
129
130
131
132 result.skippedAsVersionConflict = true;
133 if ( LOGGER.isTraceEnabled() )
134 {
135 LOGGER.trace( "Skipped resolving node: {} as version conflict",
136 ArtifactIdUtils.toId( node.getArtifact() ) );
137 }
138 }
139 else if ( cacheManager.isDuplicate( node ) )
140 {
141 if ( coordinateManager.isLeftmost( node, parents ) )
142 {
143
144
145
146
147
148 result.forceResolution = true;
149 if ( LOGGER.isTraceEnabled() )
150 {
151 LOGGER.trace( "Force resolving node: {} for scope selection",
152 ArtifactIdUtils.toId( node.getArtifact() ) );
153 }
154 }
155 else
156 {
157
158
159
160
161 result.skippedAsDuplicate = true;
162 if ( LOGGER.isTraceEnabled() )
163 {
164 LOGGER.trace( "Skipped resolving node: {} as duplicate",
165 ArtifactIdUtils.toId( node.getArtifact() ) );
166 }
167 }
168 }
169 else
170 {
171 result.resolve = true;
172 if ( LOGGER.isTraceEnabled() )
173 {
174 LOGGER.trace( "Resolving node: {}",
175 ArtifactIdUtils.toId( node.getArtifact() ) );
176 }
177 }
178
179 if ( result.toResolve() )
180 {
181 coordinateManager.updateLeftmost( node );
182 return false;
183 }
184
185 return true;
186 }
187
188 @Override
189 public void cache( DependencyNode node, List<DependencyNode> parents )
190 {
191 boolean parentForceResolution = parents.stream()
192 .anyMatch( n -> results.containsKey( n ) && results.get( n ).forceResolution );
193 if ( parentForceResolution )
194 {
195 if ( LOGGER.isTraceEnabled() )
196 {
197 LOGGER.trace( "Won't cache as node: {} inherits from a force-resolved node "
198 + "and will be omitted for duplicate", ArtifactIdUtils.toId( node.getArtifact() ) );
199 }
200 }
201 else
202 {
203 cacheManager.cacheWinner( node );
204 }
205 }
206
207 @Override
208 public void report()
209 {
210 if ( LOGGER.isTraceEnabled() )
211 {
212 LOGGER.trace( "Skipped {} nodes as duplicate",
213 results.entrySet().stream().filter( n -> n.getValue().skippedAsDuplicate ).count() );
214 LOGGER.trace( "Skipped {} nodes as having version conflict",
215 results.entrySet().stream().filter( n -> n.getValue().skippedAsVersionConflict ).count() );
216 LOGGER.trace( "Resolved {} nodes",
217 results.entrySet().stream().filter( n -> n.getValue().resolve ).count() );
218 LOGGER.trace( "Forced resolving {} nodes for scope selection",
219 results.entrySet().stream().filter( n -> n.getValue().forceResolution ).count() );
220 }
221 }
222
223 public Map<DependencyNode, DependencyResolutionResult> getResults()
224 {
225 return results;
226 }
227
228 private static final class CacheManager
229 {
230
231
232
233
234 private final Map<Artifact, DependencyNode> winners = new HashMap<>( 256 );
235
236
237
238
239
240 private final Map<String, Artifact> winnerGAs = new HashMap<>( 256 );
241
242 boolean isVersionConflict( DependencyNode node )
243 {
244 String ga = ArtifactIdUtils.toVersionlessId( node.getArtifact() );
245 if ( winnerGAs.containsKey( ga ) )
246 {
247 Artifact result = winnerGAs.get( ga );
248 return !node.getArtifact().getVersion().equals( result.getVersion() );
249 }
250
251 return false;
252 }
253
254 void cacheWinner( DependencyNode node )
255 {
256 winners.put( node.getArtifact(), node );
257 winnerGAs.put( ArtifactIdUtils.toVersionlessId( node.getArtifact() ), node.getArtifact() );
258 }
259
260 boolean isDuplicate( DependencyNode node )
261 {
262 return winners.containsKey( node.getArtifact() );
263 }
264
265 }
266
267
268 private static final class CoordinateManager
269 {
270 private final Map<Integer, AtomicInteger> sequenceGen = new HashMap<>( 256 );
271
272
273
274
275 private final Map<DependencyNode, Coordinate> coordinateMap = new HashMap<>( 256 );
276
277
278
279
280 private final Map<Artifact, Coordinate> leftmostCoordinates = new HashMap<>( 256 );
281
282
283 Coordinate getCoordinate( DependencyNode node )
284 {
285 return coordinateMap.get( node );
286 }
287
288 Coordinate createCoordinate( DependencyNode node, int depth )
289 {
290 int seq = sequenceGen.computeIfAbsent( depth, k -> new AtomicInteger() ).incrementAndGet();
291 Coordinate coordinate = new Coordinate( depth, seq );
292 coordinateMap.put( node, coordinate );
293 return coordinate;
294 }
295
296 void updateLeftmost( DependencyNode current )
297 {
298 leftmostCoordinates.put( current.getArtifact(), getCoordinate( current ) );
299 }
300
301 boolean isLeftmost( DependencyNode node, List<DependencyNode> parents )
302 {
303 Coordinate leftmost = leftmostCoordinates.get( node.getArtifact() );
304 if ( leftmost != null && leftmost.depth <= parents.size() )
305 {
306 DependencyNode sameLevelNode = parents.get( leftmost.depth - 1 );
307 return getCoordinate( sameLevelNode ).sequence < leftmost.sequence;
308 }
309
310 return false;
311 }
312 }
313
314 private static final class Coordinate
315 {
316 int depth;
317 int sequence;
318
319 Coordinate( int depth, int sequence )
320 {
321 this.depth = depth;
322 this.sequence = sequence;
323 }
324
325 @Override
326 public String toString()
327 {
328 return "{"
329 + "depth="
330 + depth
331 + ", sequence="
332 + sequence
333 + '}';
334 }
335 }
336 }
337
338
339
340
341 static final class DependencyResolutionResult
342 {
343 DependencyNode current;
344 boolean skippedAsVersionConflict;
345 boolean skippedAsDuplicate;
346 boolean resolve;
347 boolean forceResolution;
348
349 DependencyResolutionResult( DependencyNode current )
350 {
351 this.current = current;
352 }
353
354 boolean toResolve()
355 {
356 return resolve || forceResolution;
357 }
358 }
359 }