package org.apache.maven.diagrams.connectors.classes;
/*
* 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.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.diagrams.connector_api.ConnectorConfiguration;
import org.apache.maven.diagrams.connector_api.ConnectorException;
import org.apache.maven.diagrams.connector_api.DiagramConnector;
import org.apache.maven.diagrams.connector_api.DynamicDiagramConnector;
import org.apache.maven.diagrams.connector_api.descriptor.ConnectorDescriptor;
import org.apache.maven.diagrams.connectors.classes.config.ClassesConnectorConfiguration;
import org.apache.maven.diagrams.connectors.classes.config.EdgeType;
import org.apache.maven.diagrams.connectors.classes.config.ExcludeClasses;
import org.apache.maven.diagrams.connectors.classes.config.IncludeClasses;
import org.apache.maven.diagrams.connectors.classes.edge_source.EdgeSource;
import org.apache.maven.diagrams.connectors.classes.filter.ClassNamesFilter;
import org.apache.maven.diagrams.connectors.classes.filter.ExcludePattern;
import org.apache.maven.diagrams.connectors.classes.filter.FilterRepository;
import org.apache.maven.diagrams.connectors.classes.filter.IncludePattern;
import org.apache.maven.diagrams.connectors.classes.graph.ClassEdge;
import org.apache.maven.diagrams.connectors.classes.graph.ClassGraphMetadata;
import org.apache.maven.diagrams.connectors.classes.graph.ClassNode;
import org.apache.maven.diagrams.graph_api.Graph;
import org.apache.maven.diagrams.graph_api.impl.GraphImpl;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.logging.AbstractLogEnabled;
/**
* This class is main class of the Classes connector.
*
* @author <a href="mailto:ptab@newitech.com">Piotr Tabor</a>
* @version $Id$
*
*/
public class ClassesConnector extends AbstractLogEnabled implements DiagramConnector
{
private ConnectorDescriptor connectorDescriptor;
private Map<String, EdgeSource> edgeSources;
private MavenProject mavenProject;
public DynamicDiagramConnector getDynamicDiagramConnector()
{
throw new IllegalStateException( "ClassesConnector doesn't suppert dynamic diagram connector" );
}
public Graph calculateGraph( ConnectorConfiguration configuration ) throws ConnectorException
{
/* Build classpath for all available classes (from maven dependencies list) */
URL[] classpathItems =
getClassPathFromConfiguration( (ClassesConnectorConfiguration) configuration, mavenProject );
/* Create (helping) objects */
ClassModelsRepository classModelsRepository = new ClassModelsRepository( classpathItems );
ClassNodesRepository classNodesRepository =
new DefaultClassNodesRepository( classModelsRepository, (ClassesConnectorConfiguration) configuration );
ClassNamesFilter classNamesFilter =
createClassNamesFilterFromConfiguration( (ClassesConnectorConfiguration) configuration );
FilterRepository filterRepository = new FilterRepository( classNamesFilter );
/* Find all available classes's names */
List<String> classesOnClasspath;
if ( ( (ClassesConnectorConfiguration) configuration ).getExpandOnlyCurrentArtifactClasses() )
{
classesOnClasspath = calculateClassesOnClasspath( new URL[] { classpathItems[0] } );
}
else
{
classesOnClasspath = calculateClassesOnClasspath( classpathItems );
}
/* Do the "real" stuff of creating graph */
return calculateGraph( (ClassesConnectorConfiguration) configuration, classesOnClasspath, filterRepository,
classNodesRepository );
}
/**
* Calculates graph using provided "helping" objects.
*
* @param configuration
* @param classesOnClasspath
* @param filterRepository
* @param classNodesRepository
* @return
* @throws ConnectorException
*/
private Graph calculateGraph( ClassesConnectorConfiguration configuration, List<String> classesOnClasspath,
FilterRepository filterRepository, ClassNodesRepository classNodesRepository )
throws ConnectorException
{
/* ----------------------- creating nodes ---------------------- */
Set<ClassNode> resultNodes = new HashSet<ClassNode>();
/* For every not filtered class - obtains information about it and store into resultNodes */
for ( String className : classesOnClasspath )
{
if ( !filterRepository.getStatus( className ).toSkip() )
{
try
{
resultNodes.add( classNodesRepository.getClassNode( className ) );
}
catch ( ClassDataSourceException e )
{
if ( getLogger() != null )
getLogger().warn( "Cannot get full informations for " + className, e );
}
}
}
/* ----------------------- creating edges ---------------------- */
Set<ClassEdge> resultEdges = new HashSet<ClassEdge>();
/* For every available (configured) edges source - calculate edges and add them to the resultEdge */
for ( EdgeType edgeType : configuration.getEdges() )
{
EdgeSource edgeSource = getEdgeSourceByEdgeType( edgeType );
if ( edgeSource == null )
{
throw new ConnectorException( "Edge source :" + edgeType.getClass().getSimpleName() + " does not exist" );
}
edgeSource.configure( filterRepository, classNodesRepository, configuration );
resultEdges.addAll( edgeSource.calculateEdges( resultNodes ) );
}
/* Combine the resultNodes and resultEdges into the graph */
GraphImpl graph = new GraphImpl( new ClassGraphMetadata() );
graph.addNodes( resultNodes );
graph.addEdges( resultEdges );
return graph;
}
private EdgeSource getEdgeSourceByEdgeType( EdgeType edgeType )
{
return edgeSources.get( edgeType.getClass().getSimpleName() );
}
/**
*
* Returns all fully-dot-qualified classes's and interfaces's names that are available in the given classpath
*
*/
private List<String> calculateClassesOnClasspath( URL[] classpathItems )
{
List<String> classesOnClasspath = new LinkedList<String>();
for ( URL url : classpathItems )
{
try
{
classesOnClasspath.addAll( PackageUtils.getClassNamesOnClassPathItem( url ) );
}
catch ( URISyntaxException e )
{
if ( getLogger() != null )
getLogger().warn( e.getMessage() + "- skipping", e );
}
}
return classesOnClasspath;
}
/** Creates (using the configuration) the filter (to classificate the classes to add or not to add to the graph) */
private ClassNamesFilter createClassNamesFilterFromConfiguration( ClassesConnectorConfiguration configuration )
{
ClassNamesFilter result = new ClassNamesFilter();
List<IncludePattern> includes = new LinkedList<IncludePattern>();
for ( IncludeClasses include : configuration.getIncludes() )
{
includes.add( new IncludePattern( include.getPattern() ) );
}
List<ExcludePattern> excludes = new LinkedList<ExcludePattern>();
for ( ExcludeClasses exclude : configuration.getExcludes() )
{
excludes.add( new ExcludePattern( exclude.getPattern(), exclude.getKeepEdges() == null ? false
: exclude.getKeepEdges() ) );
}
result.setIncludes( includes );
result.setExcludes( excludes );
return result;
}
/**
* Gets classpath from the currentContext's mavenProject. It goes through all dependencies (all scopes) and puts
* each dependency into the classpath. The project itself's source is also added.
*/
private URL[] getClassPathFromConfiguration( ClassesConnectorConfiguration configuration, MavenProject mavenProject )
throws ConnectorException
{
ArrayList<URL> result = new ArrayList<URL>();
/* Adding current artifact's classpath */
try
{
if ( mavenProject.getArtifact() != null )
{
if ( mavenProject.getArtifact().getFile() != null )
result.add( mavenProject.getArtifact().getFile().toURI().toURL() );
else
{
if ( getLogger() != null )
{
getLogger().warn(
"No classpath attached to artifact: "
+ mavenProject.getArtifact().getDependencyConflictId() );
}
}
}
else
{
if ( getLogger() != null )
getLogger().warn( "No artifact attached to current project" );
}
}
catch ( MalformedURLException e )
{
throw new ConnectorException( e );
}
/* Adding dependencies to the classpath */
for ( Object artifact : mavenProject.getArtifactMap().values() )
{
try
{
result.add( ( (Artifact) artifact ).getFile().toURI().toURL() );
}
catch ( MalformedURLException e )
{
e.printStackTrace();
}
}
/* Translates list into arrays */
URL[] a = new URL[result.size()];
int i = 0;
for ( URL u : result )
{
a[i++] = u;
}
return a;
}
public ConnectorDescriptor getConnectorDescriptor() throws ConnectorException
{
return connectorDescriptor;
}
public void setMavenProject( MavenProject mavenProject )
{
this.mavenProject = mavenProject;
}
public void setArtifactRepository( ArtifactRepository artifactRepository )
{
/* This connector does not need artifact repository */
}
}