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

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

The newest version!
/*******************************************************************************
 * Copyright 2012 John Casey
 * 
 * Licensed 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.
 ******************************************************************************/
package org.apache.maven.graph.effective;

import static org.apache.maven.graph.effective.util.EGraphUtils.filterTerminalParents;

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 transient Set cycles = new HashSet();

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

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

    // TODO: If we construct like this based on contents of another graph, will we lose that graph's list of variable subgraphs??
    public EProjectGraph( final EProjectKey key, final Collection> relationships,
                          final Collection projectRelationships, final Set cycles )
    {
        // 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;
        if ( cycles != null )
        {
            this.cycles = cycles;
        }

        addAll( relationships );
        for ( final EProjectRelationships project : projectRelationships )
        {
            add( project );
        }
    }

    public EProjectKey getKey()
    {
        return key;
    }

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

    public Set> getFirstOrderRelationships()
    {
        final Set> rels = getExactFirstOrderRelationships();
        filterTerminalParents( rels );

        return rels;
    }

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

    public Set> getExactAllRelationships()
    {
        final Collection> rels = graph.getEdges();
        if ( rels == null )
        {
            return null;
        }

        return new HashSet>( rels );
    }

    public Set> getAllRelationships()
    {
        final Set> rels = getExactAllRelationships();
        filterTerminalParents( rels );

        return rels;
    }

    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 final Set> relationships = new HashSet>();

        private final Set projects = new HashSet();

        private Set cycles = 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 )
        {
            relationships.add( new ParentRelationship( key.getProject(), parent ) );
            return this;
        }

        public Builder withParent( final ProjectRelationship parent )
        {
            if ( parent.getDeclaring()
                       .equals( key.getProject() ) )
            {
                relationships.add( parent );
            }
            else
            {
                relationships.add( 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.relationships.clear();
            this.relationships.add( relationships.getParent() );
            this.relationships.addAll( relationships.getDependencies() );
            this.relationships.addAll( relationships.getManagedDependencies() );

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

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

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

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

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

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

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

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

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

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

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

        public Builder withExactRelationships( final Collection> relationships )
        {
            this.relationships.addAll( relationships );
            return this;
        }

        public Builder withExactRelationships( final ProjectRelationship... relationships )
        {
            this.relationships.addAll( Arrays.asList( relationships ) );
            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()
        {
            boolean foundParent = false;
            for ( final ProjectRelationship rel : relationships )
            {
                if ( rel instanceof ParentRelationship && rel.getDeclaring()
                                                             .equals( key.getProject() ) )
                {
                    foundParent = true;
                    break;
                }
            }

            if ( !foundParent )
            {
                relationships.add( new ParentRelationship( key.getProject(), key.getProject() ) );
            }

            return new EProjectGraph( key, relationships, projects, cycles );
        }

        public Builder withCycles( final Set cycles )
        {
            if ( cycles != null )
            {
                this.cycles = cycles;
            }

            return this;
        }

    }

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

        connectedProjects.add( rels.getProjectRef() );

        addAll( rels.getExactAllRelationships() );
    }

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

        incompleteSubgraphs.remove( rel.getDeclaring() );
        connectedProjects.add( rel.getDeclaring() );

        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 );
        }

        recomputeIncompleteSubgraphs();
    }

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

        connectedProjects.add( subGraph.getRoot() );

        this.connectedProjects.addAll( subGraph.connectedProjects );
        this.incompleteSubgraphs.removeAll( subGraph.connectedProjects );
        addAll( subGraph.getExactAllRelationships() );
    }

    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 ) )
                {
                    if ( !( edge instanceof ParentRelationship ) || !( (ParentRelationship) edge ).isTerminus() )
                    {
                        ProjectVersionRef target = edge.getTarget();
                        if ( target instanceof ArtifactRef )
                        {
                            target = ( (ArtifactRef) target ).asProjectVersionRef();
                        }

                        // FIXME: Are there cases where a traversal needs to see cycles??
                        boolean cycle = false;
                        for ( final ProjectRelationship item : path )
                        {
                            if ( item.getDeclaring()
                                     .equals( target ) )
                            {
                                cycle = true;
                                break;
                            }
                        }

                        if ( !cycle )
                        {
                            path.addLast( edge );
                            dfsIterate( target, traversal, path, pass );
                            path.removeLast();
                        }
                    }

                    traversal.edgeTraversed( edge, path, pass );
                }
            }
        }
    }

    // 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 )
        {
            if ( path.isEmpty() )
            {
                continue;
            }

            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 )
                {
                    // call traverseEdge no matter what, to allow traversal to "see" all relationships.
                    if ( /*( edge instanceof SelfEdge ) ||*/traversal.traverseEdge( edge, path, pass ) )
                    {
                        // Don't account for terminal parent relationship.
                        if ( !( edge instanceof ParentRelationship ) || !( (ParentRelationship) edge ).isTerminus() )
                        {
                            final List> nextPath = new ArrayList>( path );

                            // FIXME: How do we avoid cycle traversal here??
                            nextPath.add( edge );
                            nextLayer.add( nextPath );
                        }

                        traversal.edgeTraversed( edge, path, pass );
                    }
                }
            }
        }

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

    private List> getSortedOutEdges( final ProjectVersionRef node )
    {
        Collection> unsorted = graph.getOutEdges( node );
        if ( unsorted == null )
        {
            return null;
        }

        unsorted = new ArrayList>( unsorted );

        //        filterTerminalParents( unsorted );

        final List> sorted = new ArrayList>( unsorted );
        Collections.sort( sorted, new RelationshipComparator() );

        return sorted;
    }

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

    public void recomputeIncompleteSubgraphs()
    {
        for ( final ProjectVersionRef vertex : graph.getVertices() )
        {
            final Collection> outEdges = graph.getOutEdges( vertex );
            if ( outEdges != null && !outEdges.isEmpty() )
            {
                incompleteSubgraphs.remove( vertex );
            }
        }
    }

    public boolean isCycleParticipant( final ProjectVersionRef ref )
    {
        for ( final EProjectCycle cycle : cycles )
        {
            if ( cycle.contains( ref ) )
            {
                return true;
            }
        }

        return false;
    }

    public boolean isCycleParticipant( final ProjectRelationship rel )
    {
        for ( final EProjectCycle cycle : cycles )
        {
            if ( cycle.contains( rel ) )
            {
                return true;
            }
        }

        return false;
    }

    public void addCycle( final EProjectCycle cycle )
    {
        this.cycles.add( cycle );

        for ( final ProjectRelationship rel : cycle )
        {
            incompleteSubgraphs.remove( rel.getDeclaring() );
            connectedProjects.add( rel.getDeclaring() );
        }
    }

    public Set getCycles()
    {
        return new HashSet( cycles );
    }

    public Set> getRelationshipsTargeting( final ProjectVersionRef ref )
    {
        final Collection> rels = graph.getInEdges( ref );
        if ( rels == null )
        {
            return null;
        }

        return new HashSet>( rels );
    }

    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 );
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy