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
133
134 private Map<Artifact, Set<String>> buildArtifactClassMap( MavenProject project )
135 throws IOException
136 {
137 Map<Artifact, Set<String>> artifactClassMap = new LinkedHashMap<Artifact, Set<String>>();
138
139 @SuppressWarnings( "unchecked" )
140 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" ) )
147 {
148
149 JarFile jarFile = new JarFile( file );
150
151 try
152 {
153 Enumeration<JarEntry> jarEntries = jarFile.entries();
154
155 Set<String> classes = new HashSet<String>();
156
157 while ( jarEntries.hasMoreElements() )
158 {
159 String entry = jarEntries.nextElement().getName();
160 if ( entry.endsWith( ".class" ) )
161 {
162 String className = entry.replace( '/', '.' );
163 className = className.substring( 0, className.length() - ".class".length() );
164 classes.add( className );
165 }
166 }
167
168 artifactClassMap.put( artifact, classes );
169 }
170 finally
171 {
172 try
173 {
174 jarFile.close();
175 }
176 catch ( IOException ignore )
177 {
178
179 }
180 }
181 }
182 else if ( file != null && file.isDirectory() )
183 {
184 URL url = file.toURI().toURL();
185 Set<String> classes = classAnalyzer.analyze( url );
186
187 artifactClassMap.put( artifact, classes );
188 }
189 }
190
191 return artifactClassMap;
192 }
193
194 protected Set<String> buildDependencyClasses( MavenProject project )
195 throws IOException
196 {
197 Set<String> dependencyClasses = new HashSet<String>();
198
199 String outputDirectory = project.getBuild().getOutputDirectory();
200 dependencyClasses.addAll( buildDependencyClasses( outputDirectory ) );
201
202 String testOutputDirectory = project.getBuild().getTestOutputDirectory();
203 dependencyClasses.addAll( buildDependencyClasses( testOutputDirectory ) );
204
205 return dependencyClasses;
206 }
207
208 private Set<String> buildDependencyClasses( String path )
209 throws IOException
210 {
211 URL url = new File( path ).toURI().toURL();
212
213 return dependencyAnalyzer.analyze( url );
214 }
215
216 private Set<Artifact> buildDeclaredArtifacts( MavenProject project )
217 {
218 @SuppressWarnings( "unchecked" )
219 Set<Artifact> declaredArtifacts = project.getDependencyArtifacts();
220
221 if ( declaredArtifacts == null )
222 {
223 declaredArtifacts = Collections.emptySet();
224 }
225
226 return declaredArtifacts;
227 }
228
229 private Set<Artifact> buildUsedArtifacts( Map<Artifact, Set<String>> artifactClassMap,
230 Set<String> dependencyClasses )
231 {
232 Set<Artifact> usedArtifacts = new HashSet<Artifact>();
233
234 for ( String className : dependencyClasses )
235 {
236 Artifact artifact = findArtifactForClassName( artifactClassMap, className );
237
238 if ( artifact != null )
239 {
240 usedArtifacts.add( artifact );
241 }
242 }
243
244 return usedArtifacts;
245 }
246
247 protected Artifact findArtifactForClassName( Map<Artifact, Set<String>> artifactClassMap, String className )
248 {
249 for ( Map.Entry<Artifact, Set<String>> entry : artifactClassMap.entrySet() )
250 {
251 if ( entry.getValue().contains( className ) )
252 {
253 return entry.getKey();
254 }
255 }
256
257 return null;
258 }
259 }