1 package org.apache.maven.shared.dependency.analyzer;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.artifact.Artifact;
23 import org.apache.maven.project.MavenProject;
24 import org.codehaus.plexus.component.annotations.Component;
25 import org.codehaus.plexus.component.annotations.Requirement;
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.net.URL;
30 import java.util.Collections;
31 import java.util.Enumeration;
32 import java.util.HashSet;
33 import java.util.LinkedHashMap;
34 import java.util.LinkedHashSet;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.jar.JarEntry;
38 import java.util.jar.JarFile;
39
40
41
42
43
44 @Component(role = ProjectDependencyAnalyzer.class)
45 public class DefaultProjectDependencyAnalyzer
46 implements ProjectDependencyAnalyzer
47 {
48
49
50
51
52
53 @Requirement
54 private ClassAnalyzer classAnalyzer;
55
56
57
58
59 @Requirement
60 private DependencyAnalyzer dependencyAnalyzer;
61
62
63
64
65
66
67 public ProjectDependencyAnalysis analyze( MavenProject project )
68 throws ProjectDependencyAnalyzerException
69 {
70 try
71 {
72 Map<Artifact, Set<String>> artifactClassMap = buildArtifactClassMap( project );
73
74 Set<String> dependencyClasses = buildDependencyClasses( project );
75
76 Set<Artifact> declaredArtifacts = buildDeclaredArtifacts( project );
77
78 Set<Artifact> usedArtifacts = buildUsedArtifacts( artifactClassMap, dependencyClasses );
79
80 Set<Artifact> usedDeclaredArtifacts = new LinkedHashSet<Artifact>( declaredArtifacts );
81 usedDeclaredArtifacts.retainAll( usedArtifacts );
82
83 Set<Artifact> usedUndeclaredArtifacts = new LinkedHashSet<Artifact>( usedArtifacts );
84 usedUndeclaredArtifacts = removeAll( usedUndeclaredArtifacts, declaredArtifacts );
85
86 Set<Artifact> unusedDeclaredArtifacts = new LinkedHashSet<Artifact>( declaredArtifacts );
87 unusedDeclaredArtifacts = removeAll( unusedDeclaredArtifacts, usedArtifacts );
88
89 return new ProjectDependencyAnalysis( usedDeclaredArtifacts, usedUndeclaredArtifacts,
90 unusedDeclaredArtifacts );
91 }
92 catch ( IOException exception )
93 {
94 throw new ProjectDependencyAnalyzerException( "Cannot analyze dependencies", exception );
95 }
96 }
97
98
99
100
101
102
103
104
105
106
107 private Set<Artifact> removeAll( Set<Artifact> start, Set<Artifact> remove )
108 {
109 Set<Artifact> results = new LinkedHashSet<Artifact>( start.size() );
110
111 for ( Artifact artifact : start )
112 {
113 boolean found = false;
114
115 for ( Artifact artifact2 : remove )
116 {
117 if ( artifact.getDependencyConflictId().equals( artifact2.getDependencyConflictId() ) )
118 {
119 found = true;
120 break;
121 }
122 }
123
124 if ( !found )
125 {
126 results.add( artifact );
127 }
128 }
129
130 return results;
131 }
132
133
134
135 private Map<Artifact, Set<String>> buildArtifactClassMap( MavenProject project )
136 throws IOException
137 {
138 Map<Artifact, Set<String>> artifactClassMap = new LinkedHashMap<Artifact, Set<String>>();
139
140 @SuppressWarnings( "unchecked" ) Set<Artifact> dependencyArtifacts = project.getArtifacts();
141
142 for ( Artifact artifact : dependencyArtifacts )
143 {
144 File file = artifact.getFile();
145
146 if ( file != null && ( file.getName().endsWith( ".jar" ) || file.isDirectory() ) )
147 {
148
149
150 JarFile jarFile = new JarFile( file );
151
152 Enumeration<JarEntry> jarEntries = jarFile.entries();
153
154 Set<String> classes = new HashSet<String>();
155
156 while ( jarEntries.hasMoreElements() )
157 {
158 String entry = jarEntries.nextElement().getName();
159 if ( entry.endsWith( ".class" ) )
160 {
161 String className = entry.replace( '/', '.' );
162 className = className.substring( 0, className.length() - ".class".length() );
163 classes.add( className );
164
165 }
166 }
167
168
169
170 artifactClassMap.put( artifact, classes );
171 }
172 }
173
174 return artifactClassMap;
175 }
176
177 protected Set<String> buildDependencyClasses( MavenProject project )
178 throws IOException
179 {
180 Set<String> dependencyClasses = new HashSet<String>();
181
182 String outputDirectory = project.getBuild().getOutputDirectory();
183 dependencyClasses.addAll( buildDependencyClasses( outputDirectory ) );
184
185 String testOutputDirectory = project.getBuild().getTestOutputDirectory();
186 dependencyClasses.addAll( buildDependencyClasses( testOutputDirectory ) );
187
188 return dependencyClasses;
189 }
190
191 private Set<String> buildDependencyClasses( String path )
192 throws IOException
193 {
194 URL url = new File( path ).toURI().toURL();
195
196 return dependencyAnalyzer.analyze( url );
197 }
198
199 private Set<Artifact> buildDeclaredArtifacts( MavenProject project )
200 {
201 @SuppressWarnings( "unchecked" ) Set<Artifact> declaredArtifacts = project.getDependencyArtifacts();
202
203 if ( declaredArtifacts == null )
204 {
205 declaredArtifacts = Collections.emptySet();
206 }
207
208 return declaredArtifacts;
209 }
210
211 private Set<Artifact> buildUsedArtifacts( Map<Artifact, Set<String>> artifactClassMap,
212 Set<String> dependencyClasses )
213 {
214 Set<Artifact> usedArtifacts = new HashSet<Artifact>();
215
216 for ( String className : dependencyClasses )
217 {
218 Artifact artifact = findArtifactForClassName( artifactClassMap, className );
219
220 if ( artifact != null )
221 {
222 usedArtifacts.add( artifact );
223 }
224 }
225
226 return usedArtifacts;
227 }
228
229 protected Artifact findArtifactForClassName( Map<Artifact, Set<String>> artifactClassMap, String className )
230 {
231 for ( Map.Entry<Artifact, Set<String>> entry : artifactClassMap.entrySet() )
232 {
233 if ( entry.getValue().contains( className ) )
234 {
235 return entry.getKey();
236 }
237 }
238
239 return null;
240 }
241 }