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.Iterator;
28  import java.util.LinkedHashMap;
29  import java.util.LinkedHashSet;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import org.apache.maven.artifact.Artifact;
34  import org.apache.maven.project.MavenProject;
35  
36  /**
37   * 
38   * 
39   * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
40   * @version $Id: DefaultProjectDependencyAnalyzer.java 661727 2008-05-30 14:21:49Z bentmann $
41   * @plexus.component role="org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzer"
42   */
43  public class DefaultProjectDependencyAnalyzer
44      implements ProjectDependencyAnalyzer
45  {
46      // fields -----------------------------------------------------------------
47  
48      /**
49       * ClassAnalyzer
50       * 
51       * @plexus.requirement
52       */
53      private ClassAnalyzer classAnalyzer;
54  
55      /**
56       * DependencyAnalyzer
57       * 
58       * @plexus.requirement
59       */
60      private DependencyAnalyzer dependencyAnalyzer;
61  
62      // ProjectDependencyAnalyzer methods --------------------------------------
63  
64      /*
65       * @see org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzer#analyze(org.apache.maven.project.MavenProject)
66       */
67      public ProjectDependencyAnalysis analyze( MavenProject project )
68          throws ProjectDependencyAnalyzerException
69      {
70          try
71          {
72              Map artifactClassMap = buildArtifactClassMap( project );
73  
74              Set dependencyClasses = buildDependencyClasses( project );
75  
76              Set declaredArtifacts = buildDeclaredArtifacts( project );
77              
78              Set usedArtifacts = buildUsedArtifacts( artifactClassMap, dependencyClasses );
79              
80              Set usedDeclaredArtifacts = new LinkedHashSet( declaredArtifacts );
81              usedDeclaredArtifacts.retainAll( usedArtifacts );
82  
83              Set usedUndeclaredArtifacts = new LinkedHashSet( usedArtifacts );
84              usedUndeclaredArtifacts = removeAll( usedUndeclaredArtifacts, declaredArtifacts );
85  
86              Set unusedDeclaredArtifacts = new LinkedHashSet( 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       * This method defines a new way to remove the artifacts by using the
100      * conflict id. We don't care about the version here because there can be
101      * only 1 for a given artifact anyway.
102      * 
103      * @param start
104      *            initial set
105      * @param remove
106      *            set to exclude
107      * @return set with remove excluded
108      */
109     private Set removeAll( Set start, Set remove )
110     {
111         Set results = new LinkedHashSet( start.size() );
112         Iterator iter = start.iterator();
113         while ( iter.hasNext() )
114         {
115             Artifact artifact = (Artifact) iter.next();
116             Iterator iter2 = remove.iterator();
117             boolean found = false;
118             while ( iter2.hasNext() )
119             {
120                 Artifact artifact2 = (Artifact) iter2.next();
121                 if ( artifact.getDependencyConflictId().equals( artifact2.getDependencyConflictId() ) )
122                 {
123                     found = true;
124                 }
125             }
126             if ( !found )
127             {
128                 results.add( artifact );
129             }
130         }
131         return results;
132     }
133 
134     // private methods --------------------------------------------------------
135 
136     private Map buildArtifactClassMap( MavenProject project )
137         throws IOException
138     {
139         Map artifactClassMap = new LinkedHashMap();
140 
141         Set dependencyArtifacts = project.getArtifacts();
142 
143         for ( Iterator iterator = dependencyArtifacts.iterator(); iterator.hasNext(); )
144         {
145             Artifact artifact = (Artifact) iterator.next();
146 
147             File file = artifact.getFile();
148 
149             if ( file != null && file.getName().endsWith( ".jar" ) )
150             {
151                 URL url = file.toURL();
152 
153                 Set classes = classAnalyzer.analyze( url );
154 
155                 artifactClassMap.put( artifact, classes );
156             }
157         }
158 
159         return artifactClassMap;
160     }
161 
162     private Set buildDependencyClasses( MavenProject project )
163         throws IOException
164     {
165         Set dependencyClasses = new HashSet();
166         
167         String outputDirectory = project.getBuild().getOutputDirectory();
168         dependencyClasses.addAll( buildDependencyClasses( outputDirectory ) );
169         
170         String testOutputDirectory = project.getBuild().getTestOutputDirectory();
171         dependencyClasses.addAll( buildDependencyClasses( testOutputDirectory ) );
172 
173         return dependencyClasses;
174     }
175     
176     private Set buildDependencyClasses( String path )
177         throws IOException
178     {
179         URL url = new File( path ).toURI().toURL();
180 
181         return dependencyAnalyzer.analyze( url );
182     }
183     
184     private Set buildDeclaredArtifacts( MavenProject project )
185     {
186         Set declaredArtifacts = project.getDependencyArtifacts();
187         
188         if ( declaredArtifacts == null )
189         {
190             declaredArtifacts = Collections.EMPTY_SET;
191         }
192         
193         return declaredArtifacts;
194     }
195     
196     private Set buildUsedArtifacts( Map artifactClassMap, Set dependencyClasses )
197     {
198         Set usedArtifacts = new HashSet();
199 
200         for ( Iterator dependencyIterator = dependencyClasses.iterator(); dependencyIterator.hasNext(); )
201         {
202             String className = (String) dependencyIterator.next();
203 
204             Artifact artifact = findArtifactForClassName( artifactClassMap, className );
205 
206             if ( artifact != null )
207             {
208                 usedArtifacts.add( artifact );
209             }
210         }
211         
212         return usedArtifacts;
213     }
214 
215     private Artifact findArtifactForClassName( Map artifactClassMap, String className )
216     {
217         for ( Iterator artifactIterator = artifactClassMap.keySet().iterator(); artifactIterator.hasNext(); )
218         {
219             Artifact artifact = (Artifact) artifactIterator.next();
220 
221             Set artifactClassNames = (Set) artifactClassMap.get( artifact );
222 
223             if ( artifactClassNames.contains( className ) )
224             {
225                 return artifact;
226             }
227         }
228 
229         return null;
230     }
231 }