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