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.Collection;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.IdentityHashMap;
27 import java.util.Map;
28 import java.util.Set;
29 import static java.util.Objects.requireNonNull;
30
31 import org.eclipse.aether.RepositoryException;
32 import org.eclipse.aether.artifact.Artifact;
33 import org.eclipse.aether.collection.DependencyGraphTransformationContext;
34 import org.eclipse.aether.collection.DependencyGraphTransformer;
35 import org.eclipse.aether.graph.Dependency;
36 import org.eclipse.aether.graph.DependencyNode;
37
38
39
40
41
42
43
44 public final class ConflictMarker
45 implements DependencyGraphTransformer
46 {
47
48
49
50
51
52
53
54 public DependencyNode transformGraph( DependencyNode node, DependencyGraphTransformationContext context )
55 throws RepositoryException
56 {
57 requireNonNull( node, "node cannot be null" );
58 requireNonNull( context, "context cannot be null" );
59 @SuppressWarnings( "unchecked" )
60 Map<String, Object> stats = (Map<String, Object>) context.get( TransformationContextKeys.STATS );
61 long time1 = System.nanoTime();
62
63 Map<DependencyNode, Object> nodes = new IdentityHashMap<>( 1024 );
64 Map<Object, ConflictGroup> groups = new HashMap<>( 1024 );
65
66 analyze( node, nodes, groups, new int[] { 0 } );
67
68 long time2 = System.nanoTime();
69
70 Map<DependencyNode, Object> conflictIds = mark( nodes.keySet(), groups );
71
72 context.put( TransformationContextKeys.CONFLICT_IDS, conflictIds );
73
74 if ( stats != null )
75 {
76 long time3 = System.nanoTime();
77 stats.put( "ConflictMarker.analyzeTime", time2 - time1 );
78 stats.put( "ConflictMarker.markTime", time3 - time2 );
79 stats.put( "ConflictMarker.nodeCount", nodes.size() );
80 }
81
82 return node;
83 }
84
85 private void analyze( DependencyNode node, Map<DependencyNode, Object> nodes, Map<Object, ConflictGroup> groups,
86 int[] counter )
87 {
88 if ( nodes.put( node, Boolean.TRUE ) != null )
89 {
90 return;
91 }
92
93 Set<Object> keys = getKeys( node );
94 if ( !keys.isEmpty() )
95 {
96 ConflictGroup group = null;
97 boolean fixMappings = false;
98
99 for ( Object key : keys )
100 {
101 ConflictGroup g = groups.get( key );
102
103 if ( group != g )
104 {
105 if ( group == null )
106 {
107 Set<Object> newKeys = merge( g.keys, keys );
108 if ( newKeys == g.keys )
109 {
110 group = g;
111 break;
112 }
113 else
114 {
115 group = new ConflictGroup( newKeys, counter[0]++ );
116 fixMappings = true;
117 }
118 }
119 else if ( g == null )
120 {
121 fixMappings = true;
122 }
123 else
124 {
125 Set<Object> newKeys = merge( g.keys, group.keys );
126 if ( newKeys == g.keys )
127 {
128 group = g;
129 fixMappings = false;
130 break;
131 }
132 else if ( newKeys != group.keys )
133 {
134 group = new ConflictGroup( newKeys, counter[0]++ );
135 fixMappings = true;
136 }
137 }
138 }
139 }
140
141 if ( group == null )
142 {
143 group = new ConflictGroup( keys, counter[0]++ );
144 fixMappings = true;
145 }
146 if ( fixMappings )
147 {
148 for ( Object key : group.keys )
149 {
150 groups.put( key, group );
151 }
152 }
153 }
154
155 for ( DependencyNode child : node.getChildren() )
156 {
157 analyze( child, nodes, groups, counter );
158 }
159 }
160
161 private Set<Object> merge( Set<Object> keys1, Set<Object> keys2 )
162 {
163 int size1 = keys1.size();
164 int size2 = keys2.size();
165
166 if ( size1 < size2 )
167 {
168 if ( keys2.containsAll( keys1 ) )
169 {
170 return keys2;
171 }
172 }
173 else
174 {
175 if ( keys1.containsAll( keys2 ) )
176 {
177 return keys1;
178 }
179 }
180
181 Set<Object> keys = new HashSet<>();
182 keys.addAll( keys1 );
183 keys.addAll( keys2 );
184 return keys;
185 }
186
187 private Set<Object> getKeys( DependencyNode node )
188 {
189 Set<Object> keys;
190
191 Dependency dependency = node.getDependency();
192
193 if ( dependency == null )
194 {
195 keys = Collections.emptySet();
196 }
197 else
198 {
199 Object key = toKey( dependency.getArtifact() );
200
201 if ( node.getRelocations().isEmpty() && node.getAliases().isEmpty() )
202 {
203 keys = Collections.singleton( key );
204 }
205 else
206 {
207 keys = new HashSet<>();
208 keys.add( key );
209
210 for ( Artifact relocation : node.getRelocations() )
211 {
212 key = toKey( relocation );
213 keys.add( key );
214 }
215
216 for ( Artifact alias : node.getAliases() )
217 {
218 key = toKey( alias );
219 keys.add( key );
220 }
221 }
222 }
223
224 return keys;
225 }
226
227 private Map<DependencyNode, Object> mark( Collection<DependencyNode> nodes, Map<Object, ConflictGroup> groups )
228 {
229 Map<DependencyNode, Object> conflictIds = new IdentityHashMap<>( nodes.size() + 1 );
230
231 for ( DependencyNode node : nodes )
232 {
233 Dependency dependency = node.getDependency();
234 if ( dependency != null )
235 {
236 Object key = toKey( dependency.getArtifact() );
237 conflictIds.put( node, groups.get( key ).index );
238 }
239 }
240
241 return conflictIds;
242 }
243
244 private static Object toKey( Artifact artifact )
245 {
246 return new Key( artifact );
247 }
248
249 static class ConflictGroup
250 {
251
252 final Set<Object> keys;
253
254 final int index;
255
256 ConflictGroup( Set<Object> keys, int index )
257 {
258 this.keys = keys;
259 this.index = index;
260 }
261
262 @Override
263 public String toString()
264 {
265 return String.valueOf( keys );
266 }
267
268 }
269
270 static class Key
271 {
272
273 private final Artifact artifact;
274
275 Key( Artifact artifact )
276 {
277 this.artifact = artifact;
278 }
279
280 @Override
281 public boolean equals( Object obj )
282 {
283 if ( obj == this )
284 {
285 return true;
286 }
287 else if ( !( obj instanceof Key ) )
288 {
289 return false;
290 }
291 Key that = (Key) obj;
292 return artifact.getArtifactId().equals( that.artifact.getArtifactId() )
293 && artifact.getGroupId().equals( that.artifact.getGroupId() )
294 && artifact.getExtension().equals( that.artifact.getExtension() )
295 && artifact.getClassifier().equals( that.artifact.getClassifier() );
296 }
297
298 @Override
299 public int hashCode()
300 {
301 int hash = 17;
302 hash = hash * 31 + artifact.getArtifactId().hashCode();
303 hash = hash * 31 + artifact.getGroupId().hashCode();
304 hash = hash * 31 + artifact.getClassifier().hashCode();
305 hash = hash * 31 + artifact.getExtension().hashCode();
306 return hash;
307 }
308
309 @Override
310 public String toString()
311 {
312 return artifact.getGroupId() + ':' + artifact.getArtifactId() + ':' + artifact.getClassifier() + ':'
313 + artifact.getExtension();
314 }
315
316 }
317
318 }