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 }