View Javadoc
1   package org.apache.maven.report.projectinfo.dependencies;
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.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.jar.JarEntry;
29  
30  import org.apache.maven.artifact.Artifact;
31  import org.apache.maven.project.MavenProject;
32  import org.apache.maven.shared.dependency.graph.DependencyNode;
33  import org.apache.maven.shared.jar.JarAnalyzer;
34  import org.apache.maven.shared.jar.JarData;
35  import org.apache.maven.shared.jar.classes.JarClasses;
36  import org.apache.maven.shared.jar.classes.JarClassesAnalysis;
37  import org.codehaus.plexus.util.StringUtils;
38  
39  /**
40   * @version $Id: Dependencies.html 981339 2016-02-28 15:47:44Z michaelo $
41   * @since 2.1
42   */
43  public class Dependencies
44  {
45      private final MavenProject project;
46  
47      private final DependencyNode dependencyNode;
48  
49      private final JarClassesAnalysis classesAnalyzer;
50  
51      /**
52       * @since 2.1
53       */
54      private List<Artifact> projectDependencies;
55  
56      /**
57       * @since 2.1
58       */
59      private List<Artifact> projectTransitiveDependencies;
60  
61      /**
62       * @since 2.1
63       */
64      private List<Artifact> allDependencies;
65  
66      /**
67       * @since 2.1
68       */
69      private Map<String, List<Artifact>> dependenciesByScope;
70  
71      /**
72       * @since 2.1
73       */
74      private Map<String, List<Artifact>> transitiveDependenciesByScope;
75  
76      /**
77       * @since 2.1
78       */
79      private Map<String, JarData> dependencyDetails;
80  
81      /**
82       * Default constructor
83       *
84       * @param project the MavenProject.
85       * @param dependencyTreeNode the DependencyNode.
86       * @param classesAnalyzer the JarClassesAnalysis.
87       */
88      public Dependencies( MavenProject project, DependencyNode dependencyTreeNode, JarClassesAnalysis classesAnalyzer )
89      {
90          this.project = project;
91          this.dependencyNode = dependencyTreeNode;
92          this.classesAnalyzer = classesAnalyzer;
93      }
94  
95      /**
96       * Getter for the project
97       *
98       * @return the project
99       */
100     public MavenProject getProject()
101     {
102         return project;
103     }
104 
105     /**
106      * @return <code>true</code> if getProjectDependencies() is not empty, <code>false</code> otherwise.
107      */
108     public boolean hasDependencies()
109     {
110         return ( getProjectDependencies() != null ) && ( !getProjectDependencies().isEmpty() );
111     }
112 
113     /**
114      * @return a list of <code>Artifact</code> from the project.
115      */
116     public List<Artifact> getProjectDependencies()
117     {
118         if ( projectDependencies != null )
119         {
120             return projectDependencies;
121         }
122 
123         projectDependencies = new ArrayList<Artifact>();
124         for ( DependencyNode dep : dependencyNode.getChildren() )
125         {
126             projectDependencies.add( dep.getArtifact() );
127         }
128 
129         return projectDependencies;
130     }
131 
132     /**
133      * @return a list of transitive <code>Artifact</code> from the project.
134      */
135     public List<Artifact> getTransitiveDependencies()
136     {
137         if ( projectTransitiveDependencies != null )
138         {
139             return projectTransitiveDependencies;
140         }
141 
142         projectTransitiveDependencies = new ArrayList<Artifact>( getAllDependencies() );
143         projectTransitiveDependencies.removeAll( getProjectDependencies() );
144 
145         return projectTransitiveDependencies;
146     }
147 
148     /**
149      * @return a list of included <code>Artifact</code> returned by the dependency tree.
150      */
151     public List<Artifact> getAllDependencies()
152     {
153         if ( allDependencies != null )
154         {
155             return allDependencies;
156         }
157 
158         allDependencies = new ArrayList<Artifact>();
159 
160         addAllChildrenDependencies( dependencyNode );
161 
162         return allDependencies;
163     }
164 
165     /**
166      * @param isTransitively <code>true</code> to return transitive dependencies, <code>false</code> otherwise.
167      * @return a map with supported scopes as key and a list of <code>Artifact</code> as values.
168      * @see Artifact#SCOPE_COMPILE
169      * @see Artifact#SCOPE_PROVIDED
170      * @see Artifact#SCOPE_RUNTIME
171      * @see Artifact#SCOPE_SYSTEM
172      * @see Artifact#SCOPE_TEST
173      */
174     public Map<String, List<Artifact>> getDependenciesByScope( boolean isTransitively )
175     {
176         if ( isTransitively )
177         {
178             if ( transitiveDependenciesByScope != null )
179             {
180                 return transitiveDependenciesByScope;
181             }
182 
183             transitiveDependenciesByScope = new HashMap<String, List<Artifact>>();
184             for ( Artifact artifact : getTransitiveDependencies() )
185             {
186                 List<Artifact> multiValue = transitiveDependenciesByScope.get( artifact.getScope() );
187                 if ( multiValue == null )
188                 {
189                     multiValue = new ArrayList<Artifact>();
190                 }
191 
192                 if ( !multiValue.contains( artifact ) )
193                 {
194                     multiValue.add( artifact );
195                 }
196                 transitiveDependenciesByScope.put( artifact.getScope(), multiValue );
197             }
198 
199             return transitiveDependenciesByScope;
200         }
201 
202         if ( dependenciesByScope != null )
203         {
204             return dependenciesByScope;
205         }
206 
207         dependenciesByScope = new HashMap<String, List<Artifact>>();
208         for ( Artifact artifact : getProjectDependencies() )
209         {
210             List<Artifact> multiValue = dependenciesByScope.get( artifact.getScope() );
211             if ( multiValue == null )
212             {
213                 multiValue = new ArrayList<Artifact>();
214             }
215 
216             if ( !multiValue.contains( artifact ) )
217             {
218                 multiValue.add( artifact );
219             }
220             dependenciesByScope.put( artifact.getScope(), multiValue );
221         }
222 
223         return dependenciesByScope;
224     }
225 
226     /**
227      * @param artifact the artifact.
228      * @return the jardata object from the artifact
229      * @throws IOException if any
230      */
231     public JarData getJarDependencyDetails( Artifact artifact )
232         throws IOException
233     {
234         if ( dependencyDetails == null )
235         {
236             dependencyDetails = new HashMap<String, JarData>();
237         }
238 
239         JarData jarData = dependencyDetails.get( artifact.getId() );
240         if ( jarData != null )
241         {
242             return jarData;
243         }
244 
245         File file = getFile( artifact );
246 
247         if ( file.isDirectory() )
248         {
249             jarData = new JarData( artifact.getFile(), null, new ArrayList<JarEntry>() );
250 
251             jarData.setJarClasses( new JarClasses() );
252         }
253         else
254         {
255             JarAnalyzer jarAnalyzer = new JarAnalyzer( file );
256 
257             try
258             {
259                 classesAnalyzer.analyze( jarAnalyzer );
260             }
261             finally
262             {
263                 jarAnalyzer.closeQuietly();
264             }
265             
266             jarData = jarAnalyzer.getJarData();
267         }
268 
269         dependencyDetails.put( artifact.getId(), jarData );
270     
271         return jarData;
272     }
273 
274     // ----------------------------------------------------------------------
275     // Private methods
276     // ----------------------------------------------------------------------
277 
278     /**
279      * Recursive method to get all dependencies from a given <code>dependencyNode</code>
280      *
281      * @param dependencyNode not null
282      */
283     private void addAllChildrenDependencies( DependencyNode dependencyNode )
284     {
285         for ( DependencyNode subdependencyNode : dependencyNode.getChildren() )
286         {
287             Artifact artifact = subdependencyNode.getArtifact();
288 
289             if ( artifact.getGroupId().equals( project.getGroupId() )
290                 && artifact.getArtifactId().equals( project.getArtifactId() )
291                 && artifact.getVersion().equals( project.getVersion() ) )
292             {
293                 continue;
294             }
295 
296             if ( !allDependencies.contains( artifact ) )
297             {
298                 allDependencies.add( artifact );
299             }
300 
301             addAllChildrenDependencies( subdependencyNode );
302         }
303     }
304 
305     /**
306      * get the artifact's file, with detection of target/classes directory with already packaged jar available.
307      *
308      * @param artifact the artifact to retrieve the physical file
309      * @return the physical file representing the given artifact
310      */
311     public File getFile( Artifact artifact )
312     {
313         File file = artifact.getFile();
314 
315         if ( file.isDirectory() )
316         {
317             // MPIR-322: if target/classes directory, try target/artifactId-version[-classifier].jar
318             String filename = artifact.getArtifactId() + '-' + artifact.getVersion();
319             if ( StringUtils.isNotEmpty( artifact.getClassifier() ) )
320             {
321                 filename += '-' + artifact.getClassifier();
322             }
323             filename += '.' + artifact.getType();
324             
325             File jar = new File( file, "../" + filename );
326 
327             if ( jar.exists() )
328             {
329                 return jar;
330             }
331         }
332 
333         return file;
334     }
335 }