All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.graph.effective.EProjectGraph Maven / Gradle / Ivy

There is a newer version: 0.3.0
Show newest version
package org.apache.maven.graph.effective;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.maven.graph.common.ref.ArtifactRef;
import org.apache.maven.graph.common.ref.ProjectVersionRef;
import org.apache.maven.graph.effective.ref.EGraphFacts;
import org.apache.maven.graph.effective.ref.EProjectKey;
import org.apache.maven.graph.effective.rel.AbstractProjectRelationship;
import org.apache.maven.graph.effective.rel.DependencyRelationship;
import org.apache.maven.graph.effective.rel.ExtensionRelationship;
import org.apache.maven.graph.effective.rel.ParentRelationship;
import org.apache.maven.graph.effective.rel.PluginDependencyRelationship;
import org.apache.maven.graph.effective.rel.PluginRelationship;
import org.apache.maven.graph.effective.rel.ProjectRelationship;
import org.apache.maven.graph.effective.rel.RelationshipComparator;
import org.apache.maven.graph.effective.rel.RelationshipPathComparator;
import org.apache.maven.graph.effective.traverse.ProjectNetTraversal;

import edu.uci.ics.jung.graph.DirectedGraph;
import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
import edu.uci.ics.jung.graph.util.Graphs;

public class EProjectGraph
    implements EProjectNet, KeyedProjectRelationshipCollection, Serializable
{

    private static final long serialVersionUID = 1L;

    private final EProjectKey key;

    private transient Set incompleteSubgraphs = new HashSet();

    private transient Set connectedProjects = new HashSet();

    private transient Set variableSubgraphs = new HashSet();

    private final DirectedGraph> graph =
        new DirectedSparseMultigraph>();

    public EProjectGraph( final EProjectRelationships relationships )
    {
        this.key = relationships.getKey();
        add( relationships );
    }

    public EProjectGraph( final EProjectKey key, final ProjectRelationship parent,
                          final Collection dependencies,
                          final Collection plugins,
                          final Collection pluginLevelDeps,
                          final Collection extensions,
                          final Collection projectRelationships )
    {
        // NOTE: It does make sense to allow analysis of snapshots...it just requires different standards for mutability.
        //        final VersionSpec version = key.getProject()
        //                        .getVersionSpec();
        //
        //        if ( !version.isConcrete() )
        //        {
        //            throw new IllegalArgumentException(
        //                                                "Cannot build project graph rooted on non-concrete version of a project! Version is: "
        //                                                    + version );
        //        }

        this.key = key;

        add( parent );
        addAll( dependencies );
        addAll( plugins );
        addAll( pluginLevelDeps );
        addAll( extensions );
        for ( final EProjectRelationships project : projectRelationships )
        {
            add( project );
        }
    }

    public EProjectKey getKey()
    {
        return key;
    }

    public EGraphFacts getFacts()
    {
        return key.getFacts();
    }

    public Set> getFirstOrderRelationships()
    {
        return new HashSet>( graph.getOutEdges( getRoot() ) );
    }

    public Set> getAllRelationships()
    {
        return new HashSet>( graph.getEdges() );
    }

    public DirectedGraph> getRawGraph()
    {
        return Graphs.unmodifiableDirectedGraph( graph );
    }

    public boolean isComplete()
    {
        return incompleteSubgraphs.isEmpty();
    }

    public boolean isConcrete()
    {
        return variableSubgraphs.isEmpty();
    }

    public Set getIncompleteSubgraphs()
    {
        return Collections.unmodifiableSet( incompleteSubgraphs );
    }

    public Set getVariableSubgraphs()
    {
        return Collections.unmodifiableSet( variableSubgraphs );
    }

    public static final class Builder
    {
        private final EProjectKey key;

        private ProjectRelationship parent;

        private final Set dependencies = new HashSet();

        private final Set plugins = new HashSet();

        private final Set pluginLevelDeps = new HashSet();

        private final Set projects = new HashSet();

        private final Set extensions = new HashSet();

        public Builder( final EProjectRelationships rels )
        {
            this.key = rels.getKey();
            addFromDirectRelationships( rels );
        }

        public Builder( final ProjectVersionRef projectRef, final String... activeProfiles )
        {
            this.key = new EProjectKey( projectRef, new EGraphFacts( activeProfiles ) );
        }

        public Builder( final EProjectKey key )
        {
            this.key = key;
        }

        public Builder withParent( final ProjectVersionRef parent )
        {
            this.parent = new ParentRelationship( key.getProject(), parent );
            return this;
        }

        public Builder withParent( final ProjectRelationship parent )
        {
            if ( parent.getDeclaring()
                       .equals( key.getProject() ) )
            {
                this.parent = parent;
            }
            else
            {
                this.parent = parent.cloneFor( key.getProject() );
            }
            return this;
        }

        public Builder withDirectProjectRelationships( final EProjectRelationships... rels )
        {
            return withDirectProjectRelationships( Arrays.asList( rels ) );
        }

        public Builder withDirectProjectRelationships( final Collection rels )
        {
            for ( final EProjectRelationships relationships : rels )
            {
                if ( relationships.getKey()
                                  .equals( key ) )
                {
                    addFromDirectRelationships( relationships );
                }
                else
                {
                    this.projects.add( relationships );
                }
            }

            return this;
        }

        private void addFromDirectRelationships( final EProjectRelationships relationships )
        {
            this.parent = relationships.getParent();
            this.dependencies.clear();
            this.dependencies.addAll( relationships.getDependencies() );
            this.dependencies.addAll( relationships.getManagedDependencies() );

            this.plugins.clear();
            this.plugins.addAll( relationships.getPlugins() );
            this.plugins.addAll( relationships.getManagedPlugins() );

            this.extensions.clear();
            this.extensions.addAll( relationships.getExtensions() );

            this.pluginLevelDeps.clear();
            if ( relationships.getPluginDependencies() != null )
            {
                for ( final Map.Entry> entry : relationships.getPluginDependencies()
                                                                                                                   .entrySet() )
                {
                    if ( entry.getValue() != null )
                    {
                        this.pluginLevelDeps.addAll( entry.getValue() );
                    }
                }
            }
        }

        public Builder withDependencies( final List rels )
        {
            this.dependencies.addAll( rels );
            return this;
        }

        public Builder withDependencies( final DependencyRelationship... rels )
        {
            this.dependencies.addAll( Arrays.asList( rels ) );
            return this;
        }

        public Builder withPlugins( final Collection rels )
        {
            this.plugins.addAll( rels );
            return this;
        }

        public Builder withPlugins( final PluginRelationship... rels )
        {
            this.plugins.addAll( Arrays.asList( rels ) );
            return this;
        }

        public Builder withPluginLevelDependencies( final Collection rels )
        {
            this.pluginLevelDeps.addAll( rels );
            return this;
        }

        public Builder withPluginLevelDependencies( final PluginDependencyRelationship... rels )
        {
            this.pluginLevelDeps.addAll( Arrays.asList( rels ) );
            return this;
        }

        public Builder withExtensions( final Collection rels )
        {
            this.extensions.addAll( rels );
            return this;
        }

        public Builder withExtensions( final ExtensionRelationship... rels )
        {
            this.extensions.addAll( Arrays.asList( rels ) );
            return this;
        }

        public Builder withRelationships( final Collection> relationships )
        {
            final Set pluginDepRels = new HashSet();
            for ( final ProjectRelationship rel : relationships )
            {
                switch ( rel.getType() )
                {
                    case DEPENDENCY:
                    {
                        final DependencyRelationship dr = (DependencyRelationship) rel;
                        withDependencies( dr );

                        break;
                    }
                    case PLUGIN:
                    {
                        final PluginRelationship pr = (PluginRelationship) rel;
                        withPlugins( pr );

                        break;
                    }
                    case EXTENSION:
                    {
                        withExtensions( (ExtensionRelationship) rel );
                        break;
                    }
                    case PLUGIN_DEP:
                    {
                        // load all plugin relationships first.
                        pluginDepRels.add( (PluginDependencyRelationship) rel );
                        break;
                    }
                    case PARENT:
                    {
                        withParent( (ParentRelationship) rel );
                        break;
                    }
                }
            }

            withPluginLevelDependencies( pluginDepRels );

            return this;
        }

        public EProjectGraph build()
        {
            return new EProjectGraph( key, parent, dependencies, plugins, pluginLevelDeps, extensions, projects );
        }

    }

    public void add( final EProjectRelationships rels )
    {
        if ( incompleteSubgraphs.contains( rels.getProjectRef() ) )
        {
            incompleteSubgraphs.remove( rels.getProjectRef() );
        }

        connectedProjects.add( rels.getProjectRef() );

        addAll( rels.getAllRelationships() );
    }

    private > void add( final T rel )
    {
        if ( rel == null )
        {
            return;
        }

        ProjectVersionRef target = rel.getTarget();
        if ( rel instanceof DependencyRelationship )
        {
            target = ( (ArtifactRef) target ).asProjectVersionRef();
        }

        if ( !graph.containsVertex( target ) )
        {
            graph.addVertex( target );
        }

        graph.addEdge( rel, rel.getDeclaring(), target );

        if ( !target.getVersionSpec()
                    .isSingle() )
        {
            variableSubgraphs.add( target );
        }
        else if ( !connectedProjects.contains( target ) )
        {
            incompleteSubgraphs.add( target );
        }
    }

    private > void addAll( final Collection rels )
    {
        if ( rels == null )
        {
            return;
        }

        for ( final T rel : rels )
        {
            add( rel );
        }
    }

    public void connect( final EProjectGraph subGraph )
    {
        if ( incompleteSubgraphs.contains( subGraph.getRoot() ) )
        {
            incompleteSubgraphs.remove( subGraph.getRoot() );
        }

        connectedProjects.add( subGraph.getRoot() );

        this.connectedProjects.addAll( subGraph.connectedProjects );
        addAll( subGraph.getAllRelationships() );
    }

    public ProjectVersionRef getRoot()
    {
        return key.getProject();
    }

    public void traverse( final ProjectNetTraversal traversal )
    {
        final int passes = traversal.getRequiredPasses();
        for ( int i = 0; i < passes; i++ )
        {
            traversal.startTraverse( i, this );

            switch ( traversal.getType( i ) )
            {
                case breadth_first:
                {
                    bfsTraverse( traversal, i );
                    break;
                }
                case depth_first:
                {
                    dfsTraverse( traversal, i );
                    break;
                }
            }

            traversal.endTraverse( i, this );
        }
    }

    // TODO: Implement without recursion.
    private void dfsTraverse( final ProjectNetTraversal traversal, final int pass )
    {
        dfsIterate( getRoot(), traversal, new LinkedList>(), pass );
    }

    private void dfsIterate( final ProjectVersionRef node, final ProjectNetTraversal traversal,
                             final LinkedList> path, final int pass )
    {
        final List> edges = getSortedOutEdges( node );
        if ( edges != null )
        {
            for ( final ProjectRelationship edge : edges )
            {
                if ( traversal.traverseEdge( edge, path, pass ) )
                {
                    path.addLast( edge );

                    ProjectVersionRef target = edge.getTarget();
                    if ( target instanceof ArtifactRef )
                    {
                        target = ( (ArtifactRef) target ).asProjectVersionRef();
                    }

                    dfsIterate( target, traversal, path, pass );
                    path.removeLast();
                }
            }
        }
    }

    // TODO: Implement without recursion.
    private void bfsTraverse( final ProjectNetTraversal traversal, final int pass )
    {
        final List> path = new ArrayList>();
        path.add( new SelfEdge( getRoot() ) );

        bfsIterate( Collections.singletonList( path ), traversal, pass );
    }

    private void bfsIterate( final List>> thisLayer, final ProjectNetTraversal traversal,
                             final int pass )
    {
        final List>> nextLayer = new ArrayList>>();

        for ( final List> path : thisLayer )
        {
            ProjectVersionRef node = path.get( path.size() - 1 )
                                         .getTarget();
            if ( node instanceof ArtifactRef )
            {
                node = ( (ArtifactRef) node ).asProjectVersionRef();
            }

            if ( !path.isEmpty() && ( path.get( 0 ) instanceof SelfEdge ) )
            {
                path.remove( 0 );
            }

            final List> edges = getSortedOutEdges( node );
            if ( edges != null )
            {
                for ( final ProjectRelationship edge : edges )
                {
                    if ( ( edge instanceof SelfEdge ) || traversal.traverseEdge( edge, path, pass ) )
                    {
                        final List> nextPath = new ArrayList>( path );

                        nextPath.add( edge );
                        nextLayer.add( nextPath );
                    }
                }
            }
        }

        if ( !nextLayer.isEmpty() )
        {
            Collections.sort( nextLayer, new RelationshipPathComparator() );
            bfsIterate( nextLayer, traversal, pass );
        }
    }

    private List> getSortedOutEdges( final ProjectVersionRef node )
    {
        final Collection> unsorted = graph.getOutEdges( node );
        if ( unsorted != null )
        {
            final List> sorted = new ArrayList>( unsorted );
            Collections.sort( sorted, new RelationshipComparator() );

            return sorted;
        }

        return null;
    }

    private static final class SelfEdge
        extends AbstractProjectRelationship
    {

        private static final long serialVersionUID = 1L;

        SelfEdge( final ProjectVersionRef ref )
        {
            super( null, ref, ref, 0 );
        }

        @Override
        public ArtifactRef getTargetArtifact()
        {
            return new ArtifactRef( getTarget(), "pom", null, false );
        }

    }

    private void readObject( final java.io.ObjectInputStream in )
        throws IOException, ClassNotFoundException
    {
        in.defaultReadObject();
        incompleteSubgraphs = new HashSet();
        connectedProjects = new HashSet();
        variableSubgraphs = new HashSet();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy