blob: f33a1eed015133f8d4f03cdba8c1d813a5fe5f1a [file] [log] [blame]
package org.apache.maven.report.projectinfo.dependencies;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.dependency.graph.DependencyNode;
import org.apache.maven.shared.jar.JarAnalyzer;
import org.apache.maven.shared.jar.JarData;
import org.apache.maven.shared.jar.classes.JarClasses;
import org.apache.maven.shared.jar.classes.JarClassesAnalysis;
import org.codehaus.plexus.util.StringUtils;
/**
* @since 2.1
*/
public class Dependencies
{
private final MavenProject project;
private final DependencyNode dependencyNode;
private final JarClassesAnalysis classesAnalyzer;
/**
* @since 2.1
*/
private List<Artifact> projectDependencies;
/**
* @since 2.1
*/
private List<Artifact> projectTransitiveDependencies;
/**
* @since 2.1
*/
private List<Artifact> allDependencies;
/**
* @since 2.1
*/
private Map<String, List<Artifact>> dependenciesByScope;
/**
* @since 2.1
*/
private Map<String, List<Artifact>> transitiveDependenciesByScope;
/**
* @since 2.1
*/
private Map<String, JarData> dependencyDetails;
/**
* Default constructor
*
* @param project the MavenProject.
* @param dependencyTreeNode the DependencyNode.
* @param classesAnalyzer the JarClassesAnalysis.
*/
public Dependencies( MavenProject project, DependencyNode dependencyTreeNode, JarClassesAnalysis classesAnalyzer )
{
this.project = project;
this.dependencyNode = dependencyTreeNode;
this.classesAnalyzer = classesAnalyzer;
}
/**
* Getter for the project
*
* @return the project
*/
public MavenProject getProject()
{
return project;
}
/**
* @return <code>true</code> if getProjectDependencies() is not empty, <code>false</code> otherwise.
*/
public boolean hasDependencies()
{
return ( getProjectDependencies() != null ) && ( !getProjectDependencies().isEmpty() );
}
/**
* @return a list of <code>Artifact</code> from the project.
*/
public List<Artifact> getProjectDependencies()
{
if ( projectDependencies != null )
{
return projectDependencies;
}
projectDependencies = new ArrayList<>();
for ( DependencyNode dep : dependencyNode.getChildren() )
{
projectDependencies.add( dep.getArtifact() );
}
return projectDependencies;
}
/**
* @return a list of transitive <code>Artifact</code> from the project.
*/
public List<Artifact> getTransitiveDependencies()
{
if ( projectTransitiveDependencies != null )
{
return projectTransitiveDependencies;
}
projectTransitiveDependencies = new ArrayList<>( getAllDependencies() );
projectTransitiveDependencies.removeAll( getProjectDependencies() );
return projectTransitiveDependencies;
}
/**
* @return a list of included <code>Artifact</code> returned by the dependency tree.
*/
public List<Artifact> getAllDependencies()
{
if ( allDependencies != null )
{
return allDependencies;
}
allDependencies = new ArrayList<>();
addAllChildrenDependencies( dependencyNode );
return allDependencies;
}
/**
* @param isTransitively <code>true</code> to return transitive dependencies, <code>false</code> otherwise.
* @return a map with supported scopes as key and a list of <code>Artifact</code> as values.
* @see Artifact#SCOPE_COMPILE
* @see Artifact#SCOPE_PROVIDED
* @see Artifact#SCOPE_RUNTIME
* @see Artifact#SCOPE_SYSTEM
* @see Artifact#SCOPE_TEST
*/
public Map<String, List<Artifact>> getDependenciesByScope( boolean isTransitively )
{
if ( isTransitively )
{
if ( transitiveDependenciesByScope != null )
{
return transitiveDependenciesByScope;
}
transitiveDependenciesByScope = new HashMap<>();
for ( Artifact artifact : getTransitiveDependencies() )
{
List<Artifact> multiValue = transitiveDependenciesByScope.get( artifact.getScope() );
if ( multiValue == null )
{
multiValue = new ArrayList<>();
}
if ( !multiValue.contains( artifact ) )
{
multiValue.add( artifact );
}
transitiveDependenciesByScope.put( artifact.getScope(), multiValue );
}
return transitiveDependenciesByScope;
}
if ( dependenciesByScope != null )
{
return dependenciesByScope;
}
dependenciesByScope = new HashMap<>();
for ( Artifact artifact : getProjectDependencies() )
{
List<Artifact> multiValue = dependenciesByScope.get( artifact.getScope() );
if ( multiValue == null )
{
multiValue = new ArrayList<>();
}
if ( !multiValue.contains( artifact ) )
{
multiValue.add( artifact );
}
dependenciesByScope.put( artifact.getScope(), multiValue );
}
return dependenciesByScope;
}
/**
* @param artifact the artifact.
* @return the jardata object from the artifact
* @throws IOException if any
*/
public JarData getJarDependencyDetails( Artifact artifact )
throws IOException
{
if ( dependencyDetails == null )
{
dependencyDetails = new HashMap<>();
}
JarData jarData = dependencyDetails.get( artifact.getId() );
if ( jarData != null )
{
return jarData;
}
File file = getFile( artifact );
if ( file.isDirectory() )
{
jarData = new JarData( artifact.getFile(), null, new ArrayList<JarEntry>() );
jarData.setJarClasses( new JarClasses() );
}
else
{
JarAnalyzer jarAnalyzer = new JarAnalyzer( file );
try
{
classesAnalyzer.analyze( jarAnalyzer );
}
finally
{
jarAnalyzer.closeQuietly();
}
jarData = jarAnalyzer.getJarData();
}
dependencyDetails.put( artifact.getId(), jarData );
return jarData;
}
// ----------------------------------------------------------------------
// Private methods
// ----------------------------------------------------------------------
/**
* Recursive method to get all dependencies from a given <code>dependencyNode</code>
*
* @param dependencyNode not null
*/
private void addAllChildrenDependencies( DependencyNode dependencyNode )
{
for ( DependencyNode subdependencyNode : dependencyNode.getChildren() )
{
Artifact artifact = subdependencyNode.getArtifact();
if ( artifact.getGroupId().equals( project.getGroupId() )
&& artifact.getArtifactId().equals( project.getArtifactId() )
&& artifact.getVersion().equals( project.getVersion() ) )
{
continue;
}
if ( !allDependencies.contains( artifact ) )
{
allDependencies.add( artifact );
}
addAllChildrenDependencies( subdependencyNode );
}
}
/**
* get the artifact's file, with detection of target/classes directory with already packaged jar available.
*
* @param artifact the artifact to retrieve the physical file
* @return the physical file representing the given artifact
*/
public File getFile( Artifact artifact )
{
File file = artifact.getFile();
if ( file.isDirectory() )
{
// MPIR-322: if target/classes directory, try target/artifactId-version[-classifier].jar
String filename = artifact.getArtifactId() + '-' + artifact.getVersion();
if ( StringUtils.isNotEmpty( artifact.getClassifier() ) )
{
filename += '-' + artifact.getClassifier();
}
filename += '.' + artifact.getType();
File jar = new File( file, "../" + filename );
if ( jar.exists() )
{
return jar;
}
}
return file;
}
}