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 java.io.File;
23 import java.io.IOException;
24 import java.net.URL;
25 import java.util.Collections;
26 import java.util.Enumeration;
27 import java.util.HashSet;
28 import java.util.LinkedHashMap;
29 import java.util.LinkedHashSet;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.jar.JarEntry;
33 import java.util.jar.JarFile;
34
35 import org.apache.maven.artifact.Artifact;
36 import org.apache.maven.project.MavenProject;
37 import org.codehaus.plexus.component.annotations.Component;
38 import org.codehaus.plexus.component.annotations.Requirement;
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 private Set<Artifact> removeAll( Set<Artifact> start, Set<Artifact> remove )
107 {
108 Set<Artifact> results = new LinkedHashSet<Artifact>( start.size() );
109
110 for ( Artifact artifact : start )
111 {
112 boolean found = false;
113
114 for ( Artifact artifact2 : remove )
115 {
116 if ( artifact.getDependencyConflictId().equals( artifact2.getDependencyConflictId() ) )
117 {
118 found = true;
119 break;
120 }
121 }
122
123 if ( !found )
124 {
125 results.add( artifact );
126 }
127 }
128
129 return results;
130 }
131
132 protected Map<Artifact, Set<String>> buildArtifactClassMap( MavenProject project )
133 throws IOException
134 {
135 Map<Artifact, Set<String>> artifactClassMap = new LinkedHashMap<Artifact, Set<String>>();
136
137 @SuppressWarnings( "unchecked" )
138 Set<Artifact> dependencyArtifacts = project.getArtifacts();
139
140 for ( Artifact artifact : dependencyArtifacts )
141 {
142 File file = artifact.getFile();
143
144 if ( file != null && file.getName().endsWith( ".jar" ) )
145 {
146
147 JarFile jarFile = new JarFile( file );
148
149 try
150 {
151 Enumeration<JarEntry> jarEntries = jarFile.entries();
152
153 Set<String> classes = new HashSet<String>();
154
155 while ( jarEntries.hasMoreElements() )
156 {
157 String entry = jarEntries.nextElement().getName();
158 if ( entry.endsWith( ".class" ) )
159 {
160 String className = entry.replace( '/', '.' );
161 className = className.substring( 0, className.length() - ".class".length() );
162 classes.add( className );
163 }
164 }
165
166 artifactClassMap.put( artifact, classes );
167 }
168 finally
169 {
170 try
171 {
172 jarFile.close();
173 }
174 catch ( IOException ignore )
175 {
176
177 }
178 }
179 }
180 else if ( file != null && file.isDirectory() )
181 {
182 URL url = file.toURI().toURL();
183 Set<String> classes = classAnalyzer.analyze( url );
184
185 artifactClassMap.put( artifact, classes );
186 }
187 }
188
189 return artifactClassMap;
190 }
191
192 protected Set<String> buildDependencyClasses( MavenProject project )
193 throws IOException
194 {
195 Set<String> dependencyClasses = new HashSet<String>();
196
197 String outputDirectory = project.getBuild().getOutputDirectory();
198 dependencyClasses.addAll( buildDependencyClasses( outputDirectory ) );
199
200 String testOutputDirectory = project.getBuild().getTestOutputDirectory();
201 dependencyClasses.addAll( buildDependencyClasses( testOutputDirectory ) );
202
203 return dependencyClasses;
204 }
205
206 private Set<String> buildDependencyClasses( String path )
207 throws IOException
208 {
209 URL url = new File( path ).toURI().toURL();
210
211 return dependencyAnalyzer.analyze( url );
212 }
213
214 protected Set<Artifact> buildDeclaredArtifacts( MavenProject project )
215 {
216 @SuppressWarnings( "unchecked" )
217 Set<Artifact> declaredArtifacts = project.getDependencyArtifacts();
218
219 if ( declaredArtifacts == null )
220 {
221 declaredArtifacts = Collections.emptySet();
222 }
223
224 return declaredArtifacts;
225 }
226
227 protected Set<Artifact> buildUsedArtifacts( Map<Artifact, Set<String>> artifactClassMap,
228 Set<String> dependencyClasses )
229 {
230 Set<Artifact> usedArtifacts = new HashSet<Artifact>();
231
232 for ( String className : dependencyClasses )
233 {
234 Artifact artifact = findArtifactForClassName( artifactClassMap, className );
235
236 if ( artifact != null )
237 {
238 usedArtifacts.add( artifact );
239 }
240 }
241
242 return usedArtifacts;
243 }
244
245 protected Artifact findArtifactForClassName( Map<Artifact, Set<String>> artifactClassMap, String className )
246 {
247 for ( Map.Entry<Artifact, Set<String>> entry : artifactClassMap.entrySet() )
248 {
249 if ( entry.getValue().contains( className ) )
250 {
251 return entry.getKey();
252 }
253 }
254
255 return null;
256 }
257 }