1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.shared.dependency.analyzer;
20
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.LinkedHashMap;
27 import java.util.LinkedHashSet;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.stream.Collectors;
31
32 import org.apache.maven.artifact.Artifact;
33
34
35
36
37
38
39 public class ProjectDependencyAnalysis {
40
41
42 private final Map<Artifact, Set<DependencyUsage>> usedDeclaredArtifacts;
43
44 private final Map<Artifact, Set<DependencyUsage>> usedUndeclaredArtifacts;
45
46 private final Set<Artifact> unusedDeclaredArtifacts;
47
48 private final Set<Artifact> testArtifactsWithNonTestScope;
49
50
51
52
53 public ProjectDependencyAnalysis() {
54 this(null, (Map<Artifact, Set<DependencyUsage>>) null, null, null);
55 }
56
57
58
59
60
61
62
63
64 public ProjectDependencyAnalysis(
65 Set<Artifact> usedDeclaredArtifacts,
66 Set<Artifact> usedUndeclaredArtifacts,
67 Set<Artifact> unusedDeclaredArtifacts) {
68 this(usedDeclaredArtifacts, usedUndeclaredArtifacts, unusedDeclaredArtifacts, Collections.<Artifact>emptySet());
69 }
70
71
72
73
74
75
76
77
78
79 public ProjectDependencyAnalysis(
80 Set<Artifact> usedDeclaredArtifacts,
81 Set<Artifact> usedUndeclaredArtifacts,
82 Set<Artifact> unusedDeclaredArtifacts,
83 Set<Artifact> testArtifactsWithNonTestScope) {
84 this(
85 mapWithKeys(usedDeclaredArtifacts),
86 mapWithKeys(usedUndeclaredArtifacts),
87 unusedDeclaredArtifacts,
88 testArtifactsWithNonTestScope);
89 }
90
91 public ProjectDependencyAnalysis(
92 Map<Artifact, Set<DependencyUsage>> usedDeclaredArtifacts,
93 Map<Artifact, Set<DependencyUsage>> usedUndeclaredArtifacts,
94 Set<Artifact> unusedDeclaredArtifacts,
95 Set<Artifact> testArtifactsWithNonTestScope) {
96 this.usedDeclaredArtifacts = safeCopy(usedDeclaredArtifacts);
97 this.usedUndeclaredArtifacts = safeCopy(usedUndeclaredArtifacts);
98 this.unusedDeclaredArtifacts = safeCopy(unusedDeclaredArtifacts);
99 this.testArtifactsWithNonTestScope = safeCopy(testArtifactsWithNonTestScope);
100 }
101
102
103
104
105
106
107 public Set<Artifact> getUsedDeclaredArtifacts() {
108 return safeCopy(usedDeclaredArtifacts.keySet());
109 }
110
111
112
113
114
115
116 public Map<Artifact, Set<DependencyUsage>> getUsedDeclaredArtifactsWithUsages() {
117 return safeCopy(usedDeclaredArtifacts);
118 }
119
120
121
122
123
124
125 public Set<Artifact> getUsedUndeclaredArtifacts() {
126 return safeCopy(usedUndeclaredArtifacts.keySet());
127 }
128
129
130
131
132
133
134 public Map<Artifact, Set<String>> getUsedUndeclaredArtifactsWithClasses() {
135 Map<Artifact, Set<String>> usedUndeclaredArtifactsWithClasses = new HashMap<>();
136
137 for (Map.Entry<Artifact, Set<DependencyUsage>> entry : usedUndeclaredArtifacts.entrySet()) {
138 usedUndeclaredArtifactsWithClasses.put(
139 entry.getKey(),
140 entry.getValue().stream()
141 .map(DependencyUsage::getDependencyClass)
142 .collect(Collectors.toSet()));
143 }
144
145 return usedUndeclaredArtifactsWithClasses;
146 }
147
148 public Map<Artifact, Set<DependencyUsage>> getUsedUndeclaredArtifactsWithUsages() {
149 return safeCopy(usedUndeclaredArtifacts);
150 }
151
152
153
154
155
156
157 public Set<Artifact> getUnusedDeclaredArtifacts() {
158 return safeCopy(unusedDeclaredArtifacts);
159 }
160
161
162
163
164
165
166 public Set<Artifact> getTestArtifactsWithNonTestScope() {
167 return safeCopy(testArtifactsWithNonTestScope);
168 }
169
170
171
172
173
174
175
176 public ProjectDependencyAnalysis ignoreNonCompile() {
177 Set<Artifact> filteredUnusedDeclared = new HashSet<>(unusedDeclaredArtifacts);
178 filteredUnusedDeclared.removeIf(artifact -> !artifact.getScope().equals(Artifact.SCOPE_COMPILE));
179
180 return new ProjectDependencyAnalysis(
181 usedDeclaredArtifacts, usedUndeclaredArtifacts, filteredUnusedDeclared, testArtifactsWithNonTestScope);
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195 public ProjectDependencyAnalysis forceDeclaredDependenciesUsage(String[] forceUsedDependencies)
196 throws ProjectDependencyAnalyzerException {
197 Set<String> forced = new HashSet<>(Arrays.asList(forceUsedDependencies));
198
199 Set<Artifact> forcedUnusedDeclared = new HashSet<>(unusedDeclaredArtifacts);
200 Set<Artifact> forcedUsedDeclared = new HashSet<>(usedDeclaredArtifacts.keySet());
201
202 Iterator<Artifact> iter = forcedUnusedDeclared.iterator();
203 while (iter.hasNext()) {
204 Artifact artifact = iter.next();
205
206 if (forced.remove(artifact.getGroupId() + ':' + artifact.getArtifactId())) {
207
208 iter.remove();
209 forcedUsedDeclared.add(artifact);
210 }
211 }
212
213 if (!forced.isEmpty()) {
214
215 Set<String> used = new HashSet<>();
216 for (Artifact artifact : usedDeclaredArtifacts.keySet()) {
217 String id = artifact.getGroupId() + ':' + artifact.getArtifactId();
218 if (forced.remove(id)) {
219 used.add(id);
220 }
221 }
222
223 StringBuilder builder = new StringBuilder();
224 if (!forced.isEmpty()) {
225 builder.append("not declared: ").append(forced);
226 }
227 if (!used.isEmpty()) {
228 if (builder.length() > 0) {
229 builder.append(" and ");
230 }
231 builder.append("declared but already detected as used: ").append(used);
232 }
233 throw new ProjectDependencyAnalyzerException("Trying to force use of dependencies which are " + builder);
234 }
235
236 return new ProjectDependencyAnalysis(
237 mapWithKeys(forcedUsedDeclared),
238 usedUndeclaredArtifacts,
239 forcedUnusedDeclared,
240 testArtifactsWithNonTestScope);
241 }
242
243
244
245
246
247
248 @Override
249 public int hashCode() {
250 int hashCode = getUsedDeclaredArtifacts().hashCode();
251 hashCode = (hashCode * 37) + getUsedUndeclaredArtifacts().hashCode();
252 hashCode = (hashCode * 37) + getUnusedDeclaredArtifacts().hashCode();
253 hashCode = (hashCode * 37) + getTestArtifactsWithNonTestScope().hashCode();
254
255 return hashCode;
256 }
257
258
259 @Override
260 public boolean equals(Object object) {
261 if (object instanceof ProjectDependencyAnalysis) {
262 ProjectDependencyAnalysis analysis = (ProjectDependencyAnalysis) object;
263
264 return getUsedDeclaredArtifacts().equals(analysis.getUsedDeclaredArtifacts())
265 && getUsedUndeclaredArtifacts().equals(analysis.getUsedUndeclaredArtifacts())
266 && getUnusedDeclaredArtifacts().equals(analysis.getUnusedDeclaredArtifacts())
267 && getTestArtifactsWithNonTestScope().equals(analysis.getTestArtifactsWithNonTestScope());
268 }
269
270 return false;
271 }
272
273
274
275
276
277
278 @Override
279 public String toString() {
280 StringBuilder buffer = new StringBuilder();
281
282 if (!getUsedDeclaredArtifacts().isEmpty()) {
283 buffer.append("usedDeclaredArtifacts=").append(getUsedDeclaredArtifacts());
284 }
285
286 if (!getUsedUndeclaredArtifacts().isEmpty()) {
287 if (buffer.length() > 0) {
288 buffer.append(",");
289 }
290
291 buffer.append("usedUndeclaredArtifacts=").append(getUsedUndeclaredArtifacts());
292 }
293
294 if (!getUnusedDeclaredArtifacts().isEmpty()) {
295 if (buffer.length() > 0) {
296 buffer.append(",");
297 }
298
299 buffer.append("unusedDeclaredArtifacts=").append(getUnusedDeclaredArtifacts());
300 }
301
302 if (!getTestArtifactsWithNonTestScope().isEmpty()) {
303 if (buffer.length() > 0) {
304 buffer.append(",");
305 }
306
307 buffer.append("testArtifactsWithNonTestScope=").append(getTestArtifactsWithNonTestScope());
308 }
309
310 buffer.insert(0, "[");
311 buffer.insert(0, getClass().getName());
312
313 buffer.append("]");
314
315 return buffer.toString();
316 }
317
318
319
320 private Set<Artifact> safeCopy(Set<Artifact> set) {
321 return (set == null) ? Collections.emptySet() : Collections.unmodifiableSet(new LinkedHashSet<>(set));
322 }
323
324 private static Map<Artifact, Set<DependencyUsage>> safeCopy(Map<Artifact, Set<DependencyUsage>> origMap) {
325 if (origMap == null) {
326 return Collections.emptyMap();
327 }
328
329 Map<Artifact, Set<DependencyUsage>> map = new LinkedHashMap<>();
330
331 for (Map.Entry<Artifact, Set<DependencyUsage>> e : origMap.entrySet()) {
332 map.put(e.getKey(), Collections.unmodifiableSet(new LinkedHashSet<>(e.getValue())));
333 }
334
335 return map;
336 }
337
338 private static Map<Artifact, Set<DependencyUsage>> mapWithKeys(Set<Artifact> keys) {
339 if (keys == null) {
340 return Collections.emptyMap();
341 }
342
343 Map<Artifact, Set<DependencyUsage>> map = new LinkedHashMap<>();
344
345 for (Artifact k : keys) {
346 map.put(k, Collections.<DependencyUsage>emptySet());
347 }
348
349 return map;
350 }
351 }