View Javadoc

1   package org.apache.maven.shared.dependency.analyzer;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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.HashSet;
27  import java.util.LinkedHashMap;
28  import java.util.LinkedHashSet;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import org.apache.maven.artifact.Artifact;
33  import org.apache.maven.project.MavenProject;
34  import org.codehaus.plexus.component.annotations.Component;
35  import org.codehaus.plexus.component.annotations.Requirement;
36  
37  /**
38   * 
39   * 
40   * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
41   * @version $Id: DefaultProjectDependencyAnalyzer.java 1451090 2013-02-28 04:34:01Z brianf $
42   */
43  @Component( role = ProjectDependencyAnalyzer.class )
44  public class DefaultProjectDependencyAnalyzer
45      implements ProjectDependencyAnalyzer
46  {
47      // fields -----------------------------------------------------------------
48  
49      /**
50       * ClassAnalyzer
51       */
52      @Requirement
53      private ClassAnalyzer classAnalyzer;
54  
55      /**
56       * DependencyAnalyzer
57       */
58      @Requirement
59      private DependencyAnalyzer dependencyAnalyzer;
60  
61      // ProjectDependencyAnalyzer methods --------------------------------------
62  
63      /*
64       * @see org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzer#analyze(org.apache.maven.project.MavenProject)
65       */
66      public ProjectDependencyAnalysis analyze( MavenProject project )
67          throws ProjectDependencyAnalyzerException
68      {
69          try
70          {
71              Map<Artifact, Set<String>> artifactClassMap = buildArtifactClassMap( project );
72  
73              Set<String> dependencyClasses = buildDependencyClasses( project );
74  
75              Set<Artifact> declaredArtifacts = buildDeclaredArtifacts( project );
76  
77              Set<Artifact> usedArtifacts = buildUsedArtifacts( artifactClassMap, dependencyClasses );
78  
79              Set<Artifact> usedDeclaredArtifacts = new LinkedHashSet<Artifact>( declaredArtifacts );
80              usedDeclaredArtifacts.retainAll( usedArtifacts );
81  
82              Set<Artifact> usedUndeclaredArtifacts = new LinkedHashSet<Artifact>( usedArtifacts );
83              usedUndeclaredArtifacts = removeAll( usedUndeclaredArtifacts, declaredArtifacts );
84  
85              Set<Artifact> unusedDeclaredArtifacts = new LinkedHashSet<Artifact>( declaredArtifacts );
86              unusedDeclaredArtifacts = removeAll( unusedDeclaredArtifacts, usedArtifacts );
87  
88              return new ProjectDependencyAnalysis( usedDeclaredArtifacts, usedUndeclaredArtifacts,
89                                                    unusedDeclaredArtifacts );
90          }
91          catch ( IOException exception )
92          {
93              throw new ProjectDependencyAnalyzerException( "Cannot analyze dependencies", exception );
94          }
95      }
96  
97      /**
98       * This method defines a new way to remove the artifacts by using the
99       * conflict id. We don't care about the version here because there can be
100      * only 1 for a given artifact anyway.
101      * 
102      * @param start
103      *            initial set
104      * @param remove
105      *            set to exclude
106      * @return set with remove excluded
107      */
108     private Set<Artifact> removeAll( Set<Artifact> start, Set<Artifact> remove )
109     {
110         Set<Artifact> results = new LinkedHashSet<Artifact>( start.size() );
111 
112         for ( Artifact artifact : start )
113         {
114             boolean found = false;
115 
116             for ( Artifact artifact2 : remove )
117             {
118                 if ( artifact.getDependencyConflictId().equals( artifact2.getDependencyConflictId() ) )
119                 {
120                     found = true;
121                     break;
122                 }
123             }
124 
125             if ( !found )
126             {
127                 results.add( artifact );
128             }
129         }
130 
131         return results;
132     }
133 
134     // private methods --------------------------------------------------------
135 
136     private Map<Artifact, Set<String>> buildArtifactClassMap( MavenProject project )
137         throws IOException
138     {
139         Map<Artifact, Set<String>> artifactClassMap = new LinkedHashMap<Artifact, Set<String>>();
140 
141         @SuppressWarnings( "unchecked" )
142         Set<Artifact> dependencyArtifacts = project.getArtifacts();
143 
144         for ( Artifact artifact : dependencyArtifacts )
145         {
146             File file = artifact.getFile();
147 
148             if ( file != null && (file.getName().endsWith( ".jar" ) || file.isDirectory()))
149             {
150                 URL url = file.toURL();
151 
152                 Set<String> classes = classAnalyzer.analyze( url );
153 
154                 artifactClassMap.put( artifact, classes );
155             }
156         }
157 
158         return artifactClassMap;
159     }
160 
161     protected Set<String> buildDependencyClasses( MavenProject project )
162         throws IOException
163     {
164         Set<String> dependencyClasses = new HashSet<String>();
165 
166         String outputDirectory = project.getBuild().getOutputDirectory();
167         dependencyClasses.addAll( buildDependencyClasses( outputDirectory ) );
168 
169         String testOutputDirectory = project.getBuild().getTestOutputDirectory();
170         dependencyClasses.addAll( buildDependencyClasses( testOutputDirectory ) );
171 
172         return dependencyClasses;
173     }
174     
175     private Set<String> buildDependencyClasses( String path )
176         throws IOException
177     {
178         URL url = new File( path ).toURI().toURL();
179 
180         return dependencyAnalyzer.analyze( url );
181     }
182     
183     private Set<Artifact> buildDeclaredArtifacts( MavenProject project )
184     {
185         @SuppressWarnings( "unchecked" )
186         Set<Artifact> declaredArtifacts = project.getDependencyArtifacts();
187 
188         if ( declaredArtifacts == null )
189         {
190             declaredArtifacts = Collections.<Artifact>emptySet();
191         }
192 
193         return declaredArtifacts;
194     }
195     
196     private Set<Artifact> buildUsedArtifacts( Map<Artifact, Set<String>> artifactClassMap, Set<String> dependencyClasses )
197     {
198         Set<Artifact> usedArtifacts = new HashSet<Artifact>();
199 
200         for ( String className : dependencyClasses )
201         {
202             Artifact artifact = findArtifactForClassName( artifactClassMap, className );
203 
204             if ( artifact != null )
205             {
206                 usedArtifacts.add( artifact );
207             }
208         }
209 
210         return usedArtifacts;
211     }
212 
213     protected Artifact findArtifactForClassName( Map<Artifact, Set<String>> artifactClassMap, String className )
214     {
215         for ( Map.Entry<Artifact, Set<String>> entry : artifactClassMap.entrySet() )
216         {
217             if ( entry.getValue().contains( className ) )
218             {
219                 return entry.getKey();
220             }
221         }
222 
223         return null;
224     }
225 }