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