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 @SuppressWarnings("UnusedReturnValue")
196 public ProjectDependencyAnalysis forceDeclaredDependenciesUsage(String[] forceUsedDependencies)
197 throws ProjectDependencyAnalyzerException {
198 Set<String> forced = new HashSet<>(Arrays.asList(forceUsedDependencies));
199
200 Set<Artifact> forcedUnusedDeclared = new HashSet<>(unusedDeclaredArtifacts);
201 Set<Artifact> forcedUsedDeclared = new HashSet<>(usedDeclaredArtifacts.keySet());
202
203 Iterator<Artifact> iter = forcedUnusedDeclared.iterator();
204 while (iter.hasNext()) {
205 Artifact artifact = iter.next();
206
207 if (forced.remove(artifact.getGroupId() + ':' + artifact.getArtifactId())) {
208
209 iter.remove();
210 forcedUsedDeclared.add(artifact);
211 }
212 }
213
214 if (!forced.isEmpty()) {
215
216 Set<String> used = new HashSet<>();
217 for (Artifact artifact : usedDeclaredArtifacts.keySet()) {
218 String id = artifact.getGroupId() + ':' + artifact.getArtifactId();
219 if (forced.remove(id)) {
220 used.add(id);
221 }
222 }
223
224 StringBuilder builder = new StringBuilder();
225 if (!forced.isEmpty()) {
226 builder.append("not declared: ").append(forced);
227 }
228 if (!used.isEmpty()) {
229 if (builder.length() > 0) {
230 builder.append(" and ");
231 }
232 builder.append("declared but already detected as used: ").append(used);
233 }
234 throw new ProjectDependencyAnalyzerException("Trying to force use of dependencies which are " + builder);
235 }
236
237 return new ProjectDependencyAnalysis(
238 mapWithKeys(forcedUsedDeclared),
239 usedUndeclaredArtifacts,
240 forcedUnusedDeclared,
241 testArtifactsWithNonTestScope);
242 }
243
244
245
246
247
248
249 @Override
250 public int hashCode() {
251 int hashCode = getUsedDeclaredArtifacts().hashCode();
252 hashCode = (hashCode * 37) + getUsedUndeclaredArtifacts().hashCode();
253 hashCode = (hashCode * 37) + getUnusedDeclaredArtifacts().hashCode();
254 hashCode = (hashCode * 37) + getTestArtifactsWithNonTestScope().hashCode();
255
256 return hashCode;
257 }
258
259
260 @Override
261 public boolean equals(Object object) {
262 if (object instanceof ProjectDependencyAnalysis) {
263 ProjectDependencyAnalysis analysis = (ProjectDependencyAnalysis) object;
264
265 return getUsedDeclaredArtifacts().equals(analysis.getUsedDeclaredArtifacts())
266 && getUsedUndeclaredArtifacts().equals(analysis.getUsedUndeclaredArtifacts())
267 && getUnusedDeclaredArtifacts().equals(analysis.getUnusedDeclaredArtifacts())
268 && getTestArtifactsWithNonTestScope().equals(analysis.getTestArtifactsWithNonTestScope());
269 }
270
271 return false;
272 }
273
274
275
276
277
278
279 @Override
280 public String toString() {
281 StringBuilder buffer = new StringBuilder();
282
283 if (!getUsedDeclaredArtifacts().isEmpty()) {
284 buffer.append("usedDeclaredArtifacts=").append(getUsedDeclaredArtifacts());
285 }
286
287 if (!getUsedUndeclaredArtifacts().isEmpty()) {
288 if (buffer.length() > 0) {
289 buffer.append(",");
290 }
291
292 buffer.append("usedUndeclaredArtifacts=").append(getUsedUndeclaredArtifacts());
293 }
294
295 if (!getUnusedDeclaredArtifacts().isEmpty()) {
296 if (buffer.length() > 0) {
297 buffer.append(",");
298 }
299
300 buffer.append("unusedDeclaredArtifacts=").append(getUnusedDeclaredArtifacts());
301 }
302
303 if (!getTestArtifactsWithNonTestScope().isEmpty()) {
304 if (buffer.length() > 0) {
305 buffer.append(",");
306 }
307
308 buffer.append("testArtifactsWithNonTestScope=").append(getTestArtifactsWithNonTestScope());
309 }
310
311 buffer.insert(0, "[");
312 buffer.insert(0, getClass().getName());
313
314 buffer.append("]");
315
316 return buffer.toString();
317 }
318
319
320
321 private Set<Artifact> safeCopy(Set<Artifact> set) {
322 return (set == null) ? Collections.emptySet() : Collections.unmodifiableSet(new LinkedHashSet<>(set));
323 }
324
325 private static Map<Artifact, Set<DependencyUsage>> safeCopy(Map<Artifact, Set<DependencyUsage>> origMap) {
326 if (origMap == null) {
327 return Collections.emptyMap();
328 }
329
330 Map<Artifact, Set<DependencyUsage>> map = new LinkedHashMap<>();
331
332 for (Map.Entry<Artifact, Set<DependencyUsage>> e : origMap.entrySet()) {
333 map.put(e.getKey(), Collections.unmodifiableSet(new LinkedHashSet<>(e.getValue())));
334 }
335
336 return map;
337 }
338
339 private static Map<Artifact, Set<DependencyUsage>> mapWithKeys(Set<Artifact> keys) {
340 if (keys == null) {
341 return Collections.emptyMap();
342 }
343
344 Map<Artifact, Set<DependencyUsage>> map = new LinkedHashMap<>();
345
346 for (Artifact k : keys) {
347 map.put(k, Collections.<DependencyUsage>emptySet());
348 }
349
350 return map;
351 }
352 }