From aed0faef9a7970553576776a31831713fe949048 Mon Sep 17 00:00:00 2001 From: John Casey Date: Tue, 29 Oct 2013 02:08:05 -0500 Subject: [PATCH 1/2] Fixing jung implementation, and starting optimization...hoping to add storage and maybe have something better than the neo4j impl, since it has less overhead. --- .../spi/jung/CycleDetectionTraversal.java | 77 ++ .../graph/spi/jung/JungEGraphDriver.java | 723 ++++++++++-------- .../graph/spi/jung/PathMappingTraversal.java | 56 ++ .../maven/atlas/graph/spi/jung/PathState.java | 137 ++++ .../jung/SingleCycleDetectionTraversal.java | 81 ++ .../spi/neo4j/AbstractNeo4JEGraphDriver.java | 40 +- .../maven/atlas/graph/filter/AndFilter.java | 7 + .../maven/atlas/graph/filter/OrFilter.java | 7 + .../atlas/graph/model/EProjectCycle.java | 18 +- .../atlas/graph/model/EProjectGraph.java | 8 +- .../maven/atlas/graph/model/EProjectWeb.java | 11 +- .../rel/AbstractProjectRelationship.java | 69 +- .../atlas/graph/spi/GraphDatabaseDriver.java | 6 +- .../traverse/AbstractFilteringTraversal.java | 20 +- .../graph/traverse/AbstractTraversal.java | 31 +- .../graph/traverse/BuildOrderTraversal.java | 75 +- .../graph/traverse/ProjectNetTraversal.java | 7 +- .../print/StructurePrintingTraversal.java | 36 +- .../atlas/tck/graph/CycleDetectionTCK.java | 1 - .../traverse/BuildOrderTraversalTCK.java | 12 +- 20 files changed, 950 insertions(+), 472 deletions(-) create mode 100644 drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTraversal.java create mode 100644 drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/PathMappingTraversal.java create mode 100644 drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/PathState.java create mode 100644 drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/SingleCycleDetectionTraversal.java diff --git a/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTraversal.java b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTraversal.java new file mode 100644 index 00000000..f20eb879 --- /dev/null +++ b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTraversal.java @@ -0,0 +1,77 @@ +package org.commonjava.maven.atlas.graph.spi.jung; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.commonjava.maven.atlas.graph.model.EProjectCycle; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.graph.traverse.AbstractTraversal; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; +import org.commonjava.util.logging.Logger; + +final class CycleDetectionTraversal + extends AbstractTraversal +{ + private final Logger logger = new Logger( getClass() ); + + private final Set cycles = new HashSet(); + + private final Set> hits = new HashSet<>(); + + public Set> getCycleInjectors() + { + return hits; + } + + public Set getCycles() + { + return cycles; + } + + @Override + public boolean preCheck( final ProjectRelationship relationship, final List> path, final int pass ) + { + boolean found = false; + int i = 0; + for ( final ProjectRelationship rel : path ) + { + final ProjectVersionRef from = rel.getDeclaring(); + + if ( from.equals( relationship.getTarget() + .asProjectVersionRef() ) ) + { + final List> sub = path.subList( i, path.size() ); + final List> cycle = new ArrayList>( sub ); + cycle.add( relationship ); + + logger.info( "Found cycle: %s", cycle ); + + cycles.add( new EProjectCycle( cycle ) ); + hits.add( relationship ); + + found = true; + } + + i++; + } + + if ( relationship.getDeclaring() + .equals( relationship.getTarget() + .asProjectVersionRef() ) ) + { + final List> cycle = new ArrayList>(); + cycle.add( relationship ); + + logger.info( "Found cycle: %s", cycle ); + + cycles.add( new EProjectCycle( cycle ) ); + hits.add( relationship ); + + found = true; + } + + return !found; + } +} \ No newline at end of file diff --git a/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/JungEGraphDriver.java b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/JungEGraphDriver.java index 86d6c1d0..b7df260b 100644 --- a/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/JungEGraphDriver.java +++ b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/JungEGraphDriver.java @@ -28,15 +28,17 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import org.apache.commons.lang.StringUtils; +import org.commonjava.maven.atlas.graph.filter.OrFilter; import org.commonjava.maven.atlas.graph.filter.ProjectRelationshipFilter; import org.commonjava.maven.atlas.graph.model.EProjectCycle; -import org.commonjava.maven.atlas.graph.model.EProjectNet; import org.commonjava.maven.atlas.graph.model.GraphView; import org.commonjava.maven.atlas.graph.rel.AbstractProjectRelationship; import org.commonjava.maven.atlas.graph.rel.ParentRelationship; @@ -46,7 +48,7 @@ import org.commonjava.maven.atlas.graph.rel.RelationshipType; import org.commonjava.maven.atlas.graph.spi.GraphDatabaseDriver; import org.commonjava.maven.atlas.graph.spi.GraphDriverException; -import org.commonjava.maven.atlas.graph.traverse.AbstractTraversal; +import org.commonjava.maven.atlas.graph.traverse.FilteringTraversal; import org.commonjava.maven.atlas.graph.traverse.ProjectNetTraversal; import org.commonjava.maven.atlas.graph.util.RelationshipUtils; import org.commonjava.maven.atlas.graph.workspace.GraphWorkspace; @@ -54,6 +56,7 @@ import org.commonjava.maven.atlas.ident.ref.ArtifactRef; import org.commonjava.maven.atlas.ident.ref.ProjectRef; import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; +import org.commonjava.maven.atlas.ident.version.InvalidVersionSpecificationException; import org.commonjava.maven.atlas.ident.version.SingleVersion; import org.commonjava.util.logging.Logger; @@ -63,61 +66,117 @@ public class JungEGraphDriver implements GraphDatabaseDriver { - // private final Logger logger = new Logger( getClass() ); + private static final String PATHS = "paths"; - private DirectedGraph> graph = new DirectedSparseMultigraph<>(); + private final Logger logger = new Logger( getClass() ); + + private final DirectedGraph> graph = new DirectedSparseMultigraph<>(); private final Map> byGA = new HashMap<>(); - private transient Set incompleteSubgraphs = new HashSet<>(); + private transient Map selectedForAll = new HashMap<>(); - private transient Set variableSubgraphs = new HashSet<>(); + private final Set cycles = new HashSet(); - private transient Map selected = new HashMap<>(); + private final Set incompleteSubgraphs = new HashSet<>(); - private transient Map selectedForAll = new HashMap<>(); + private final Set variableSubgraphs = new HashSet<>(); - private final Map> metadataOwners = new HashMap<>(); + private final Map selected = new HashMap<>(); private final Map> metadata = new HashMap<>(); - private final Set cycles = new HashSet(); + private final Map> metadataOwners = new HashMap<>(); + + private transient Map> caches = new WeakHashMap<>(); @Override public Collection> getRelationshipsDeclaredBy( final GraphView view, final ProjectVersionRef ref ) { - return imposeSelections( view, graph.getOutEdges( ref.asProjectVersionRef() ) ); + final Map pathStates = getPathStates( view ); + + findPathsToProjects( getRoots( view, false ), view.getFilter(), view, false, pathStates, ref ); + + final PathState state = pathStates.get( ref ); + if ( state == null ) + { + return null; + } + + final Collection filters = state.getVisiblePathFilters(); + final OrFilter filter = filters == null || filters.isEmpty() ? null : new OrFilter( filters ); + + final Collection> edges = new HashSet<>( graph.getOutEdges( ref.asProjectVersionRef() ) ); + for ( final Iterator> iterator = edges.iterator(); iterator.hasNext(); ) + { + final ProjectRelationship rel = iterator.next(); + if ( filter != null && !filter.accept( rel ) ) + { + iterator.remove(); + } + } + + return imposeSelections( view, edges, false ); } @Override public Collection> getRelationshipsTargeting( final GraphView view, final ProjectVersionRef ref ) { - return imposeSelections( view, graph.getInEdges( ref.asProjectVersionRef() ) ); + final Collection> edges = new HashSet<>( graph.getInEdges( ref.asProjectVersionRef() ) ); + + final Map pathStates = getPathStates( view ); + + findPathsToRelationships( getRoots( view, false ), view.getFilter(), view, false, pathStates, + edges.toArray( new ProjectRelationship[edges.size()] ) ); + + final PathState state = pathStates.get( ref ); + return state.getVisibleTargetingRelationships(); } @Override public Collection> getAllRelationships( final GraphView view ) { - return imposeSelections( view, graph.getEdges() ); + final Set roots = getRoots( view, false ); + if ( roots != null && !roots.isEmpty() ) + { + final FilteringTraversal traversal = new FilteringTraversal( view.getFilter(), true ); + + // FIXME: if the view doesn't have any roots, this will fail. + dfsTraverse( view, traversal, 0, view.getRoots() + .toArray( new ProjectVersionRef[view.getRoots() + .size()] ) ); + + return traversal.getCapturedRelationships(); + } + + return imposeSelections( view, graph.getEdges(), true ); } - private Collection> imposeSelections( final GraphView view, final Collection> edges ) + private Collection> imposeSelections( final GraphView view, final Collection> edges, + final boolean doFilter ) { if ( edges == null || edges.isEmpty() ) { return edges; } - final GraphWorkspace workspace = view.getWorkspace(); - if ( workspace == null ) + if ( view == null || ( view.getWorkspace() == null && ( !doFilter || view.getFilter() == null ) ) ) { // no selections here... return edges; } + final GraphWorkspace workspace = view.getWorkspace(); + final ProjectRelationshipFilter filter = doFilter ? view.getFilter() : null; + final List> result = new ArrayList>( edges.size() ); for ( final ProjectRelationship edge : edges ) { + if ( filter != null && !filter.accept( edge ) ) + { + continue; + } + final ProjectVersionRef target = edge.getTarget() .asProjectVersionRef(); final ProjectVersionRef selected = getSelectedVersion( target ); @@ -192,32 +251,46 @@ private ProjectVersionRef getSelectedVersion( final ProjectVersionRef ref ) public Set> addRelationships( final ProjectRelationship... rels ) { final Set> skipped = new HashSet>(); + final Set targets = new HashSet<>( rels.length ); for ( final ProjectRelationship rel : rels ) { + final ProjectVersionRef target = rel.getTarget() + .asProjectVersionRef(); + + targets.add( target ); + + try + { + if ( !target.getVersionSpec() + .isSingle() ) + { + logger.info( "Adding variable target: %s", target ); + variableSubgraphs.add( target ); + } + } + catch ( final InvalidVersionSpecificationException e ) + { + logger.error( "Failed to determine variable-version status of: %s. Reason: %s", e, target, e.getMessage() ); + skipped.add( rel ); + continue; + } + if ( !graph.containsVertex( rel.getDeclaring() ) ) { - // // logger.info( "Adding node: %s", rel.getDeclaring() ); + logger.info( "Adding node: %s", rel.getDeclaring() ); graph.addVertex( rel.getDeclaring() ); addGA( rel.getDeclaring() ); } - final ProjectVersionRef target = rel.getTarget() - .asProjectVersionRef(); - if ( !target.getVersionSpec() - .isSingle() ) - { - // logger.info( "Adding variable target: %s", target ); - variableSubgraphs.add( target ); - } - else if ( !graph.containsVertex( target ) ) + if ( !graph.containsVertex( target ) ) { - // logger.info( "Adding incomplete target: %s", target ); + logger.info( "Adding incomplete target: %s", target ); incompleteSubgraphs.add( target ); } if ( !graph.containsVertex( target ) ) { - // logger.info( "Adding node: %s", target ); + logger.info( "Adding node: %s", target ); graph.addVertex( target.asProjectVersionRef() ); addGA( target ); } @@ -225,7 +298,7 @@ else if ( !graph.containsVertex( target ) ) final List> edges = new ArrayList>( graph.findEdgeSet( rel.getDeclaring(), target ) ); if ( !edges.contains( rel ) ) { - // logger.info( "Adding edge: %s -> %s", rel.getDeclaring(), target ); + logger.info( "Adding edge: %s -> %s", rel.getDeclaring(), target ); graph.addEdge( rel, rel.getDeclaring(), target.asProjectVersionRef() ); } else @@ -233,44 +306,61 @@ else if ( !graph.containsVertex( target ) ) final int idx = edges.indexOf( rel ); final ProjectRelationship existing = edges.get( idx ); - // logger.info( "Adding sources: %s to existing edge: %s", rel.getSources(), existing ); + logger.info( "Adding sources: %s to existing edge: %s", rel.getSources(), existing ); existing.addSources( rel.getSources() ); } - // logger.info( "removing from incomplete status: %s", rel.getDeclaring() ); + logger.info( "removing from incomplete status: %s", rel.getDeclaring() ); incompleteSubgraphs.remove( rel.getDeclaring() ); } - for ( final ProjectRelationship rel : rels ) - { - if ( skipped.contains( rel ) ) - { - continue; - } + final CycleDetectionTraversal traversal = new CycleDetectionTraversal(); + dfsTraverse( GraphView.GLOBAL, traversal, 0, targets.toArray( new ProjectVersionRef[targets.size()] ) ); - // logger.info( "Detecting cycles introduced by: %s", rel ); + logger.info( "Detecting cycles..." ); - final CycleDetectionTraversal traversal = new CycleDetectionTraversal( rel ); + final Collection cycles = traversal.getCycles(); - dfsTraverse( GraphView.GLOBAL, traversal, 0, rel.getTarget() - .asProjectVersionRef() ); + if ( !cycles.isEmpty() ) + { + logger.info( "%d CYCLES found!\n %s", cycles.size(), join( cycles, "\n " ) ); - final List cycles = traversal.getCycles(); + this.cycles.addAll( cycles ); - if ( !cycles.isEmpty() ) + for ( final ProjectRelationship rel : traversal.getCycleInjectors() ) { - // logger.info( "CYCLE introduced by: %s", rel ); skipped.add( rel ); - graph.removeEdge( rel ); - this.cycles.addAll( cycles ); } } + clearAddedTargetStates( rels, skipped ); + return skipped; } + private void clearAddedTargetStates( final ProjectRelationship[] rels, final Set> skipped ) + { + for ( final GraphView view : caches.keySet() ) + { + final Map pathStates = getPathStates( view, false ); + if ( pathStates != null ) + { + for ( final ProjectRelationship rel : rels ) + { + if ( skipped.contains( rel ) ) + { + continue; + } + + pathStates.remove( rel.getTarget() + .asProjectVersionRef() ); + } + } + } + } + private boolean addGA( final ProjectVersionRef ref ) { final ProjectRef pr = ref.asProjectRef(); @@ -287,27 +377,117 @@ private boolean addGA( final ProjectVersionRef ref ) @Override public Set>> getAllPathsTo( final GraphView view, final ProjectVersionRef... refs ) { - final PathDetectionTraversal traversal = new PathDetectionTraversal( refs ); - - final Set roots = view.getRoots(); - if ( roots == null ) + if ( view.getRoots() == null || view.getRoots() + .isEmpty() ) { - new Logger( getClass() ).warn( "Cannot retrieve paths targeting %s. No roots specified for this project network!", join( refs, ", " ) ); + logger.warn( "Cannot retrieve paths targeting %s. No roots specified for this project network!", join( refs, ", " ) ); return null; } - for ( final ProjectVersionRef root : roots ) + final Map pathStates = getPathStates( view ); + findPathsToProjects( view.getRoots(), view.getFilter(), view, false, pathStates, refs ); + + final Set>> result = new HashSet<>(); + for ( final ProjectVersionRef ref : refs ) + { + final PathState state = pathStates.get( ref ); + if ( state != null ) + { + result.addAll( state.getVisiblePaths() ); + } + } + + return result; + } + + private void findPathsToProjects( final Set roots, final ProjectRelationshipFilter filter, final GraphView view, + final boolean detectOnly, final Map pathStates, + final Collection refs ) + { + boolean reTraverse = false; + for ( final ProjectVersionRef ref : refs ) + { + if ( !pathStates.containsKey( ref ) ) + { + reTraverse = true; + break; + } + } + + if ( reTraverse ) + { + final PathMappingTraversal traversal = new PathMappingTraversal( filter, pathStates ); + + if ( roots == null || roots.isEmpty() ) + { + logger.warn( "Cannot retrieve paths targeting %s. No roots specified for this project network!", join( refs, ", " ) ); + return; + } + + dfsTraverse( view, traversal, 0, roots.toArray( new ProjectVersionRef[roots.size()] ) ); + } + } + + private void findPathsToProjects( final Set roots, final ProjectRelationshipFilter filter, final GraphView view, + final boolean detectOnly, final Map pathStates, final ProjectVersionRef... refs ) + { + boolean reTraverse = false; + for ( final ProjectVersionRef ref : refs ) { - dfsTraverse( view, traversal, 0, root ); + if ( !pathStates.containsKey( ref ) ) + { + reTraverse = true; + break; + } + } + + if ( reTraverse ) + { + final FilteringTraversal traversal = new FilteringTraversal( filter, true ); + + if ( roots == null || roots.isEmpty() ) + { + logger.warn( "Cannot retrieve paths targeting %s. No roots specified for this project network!", join( refs, ", " ) ); + return; + } + + dfsTraverse( view, traversal, 0, roots.toArray( new ProjectVersionRef[roots.size()] ) ); + } + } + + private void findPathsToRelationships( final Set roots, final ProjectRelationshipFilter filter, final GraphView view, + final boolean detectOnly, final Map pathStates, + final ProjectRelationship... rels ) + { + boolean reTraverse = false; + for ( final ProjectRelationship rel : rels ) + { + if ( !pathStates.containsKey( rel.getTarget() + .asProjectVersionRef() ) ) + { + reTraverse = true; + break; + } } - return traversal.getPaths(); + if ( reTraverse ) + { + final FilteringTraversal traversal = new FilteringTraversal( filter, true ); + + if ( roots == null || roots.isEmpty() ) + { + logger.warn( "Cannot retrieve paths targeting %s. No roots specified for this project network!", join( rels, ", " ) ); + return; + } + + dfsTraverse( view, traversal, 0, roots.toArray( new ProjectVersionRef[roots.size()] ) ); + } } @Override public boolean introducesCycle( final GraphView view, final ProjectRelationship rel ) { - final CycleDetectionTraversal traversal = new CycleDetectionTraversal( rel ); + final SingleCycleDetectionTraversal traversal = new SingleCycleDetectionTraversal( rel ); dfsTraverse( view, traversal, 0, rel.getTarget() .asProjectVersionRef() ); @@ -319,40 +499,60 @@ public boolean introducesCycle( final GraphView view, final ProjectRelationship< @Override public Set getAllProjects( final GraphView view ) { - return new HashSet( graph.getVertices() ); + return findVisibleProjects( getRoots( view, false ), view.getFilter(), view, graph.getVertices() ); } @Override - public void traverse( final GraphView view, final ProjectNetTraversal traversal, final EProjectNet net, final ProjectVersionRef root ) + public void traverse( final GraphView view, final ProjectNetTraversal traversal, final ProjectVersionRef... roots ) throws GraphDriverException { - final int passes = traversal.getRequiredPasses(); - for ( int i = 0; i < passes; i++ ) + ProjectVersionRef[] start; + if ( roots.length > 0 ) + { + start = roots; + } + else if ( view.getRoots() != null && !view.getRoots() + .isEmpty() ) { - traversal.startTraverse( i, net ); + start = view.getRoots() + .toArray( new ProjectVersionRef[view.getRoots() + .size()] ); + } + else + { + logger.error( "Cannot traverse; no roots specified!" ); + return; + } + + for ( int i = 0; i < traversal.getRequiredPasses(); i++ ) + { + traversal.startTraverse( i ); switch ( traversal.getType( i ) ) { case breadth_first: { - bfsTraverse( view, traversal, i, root ); + bfsTraverse( view, traversal, i, start ); break; } case depth_first: { - dfsTraverse( view, traversal, i, root ); + dfsTraverse( view, traversal, i, start ); break; } } - traversal.endTraverse( i, net ); + traversal.endTraverse( i ); } } // TODO: Implement without recursion. - private void dfsTraverse( final GraphView view, final ProjectNetTraversal traversal, final int pass, final ProjectVersionRef root ) + private void dfsTraverse( final GraphView view, final ProjectNetTraversal traversal, final int pass, final ProjectVersionRef... roots ) { - dfsIterate( view, root, traversal, new LinkedList>(), pass ); + for ( final ProjectVersionRef root : roots ) + { + dfsIterate( view, root, traversal, new LinkedList>(), pass ); + } } private void dfsIterate( final GraphView view, final ProjectVersionRef node, final ProjectNetTraversal traversal, @@ -363,12 +563,13 @@ private void dfsIterate( final GraphView view, final ProjectVersionRef node, fin { for ( final ProjectRelationship edge : edges ) { - path.addLast( edge ); if ( traversal.traverseEdge( edge, path, pass ) ) { if ( !( edge instanceof ParentRelationship ) || !( (ParentRelationship) edge ).isTerminus() ) { + path.addLast( edge ); + final ProjectVersionRef target = edge.getTarget() .asProjectVersionRef(); @@ -389,23 +590,32 @@ private void dfsIterate( final GraphView view, final ProjectVersionRef node, fin dfsIterate( view, target, traversal, path, pass ); } + path.removeLast(); } traversal.edgeTraversed( edge, path, pass ); + if ( traversal.isStopped() ) + { + return; + } } - path.removeLast(); } } } // TODO: Implement without recursion. - private void bfsTraverse( final GraphView view, final ProjectNetTraversal traversal, final int pass, final ProjectVersionRef root ) + private void bfsTraverse( final GraphView view, final ProjectNetTraversal traversal, final int pass, final ProjectVersionRef... roots ) { - final List> path = new ArrayList>(); - path.add( new SelfEdge( root ) ); + final List>> starts = new ArrayList<>(); + for ( final ProjectVersionRef root : roots ) + { + final List> path = new ArrayList>(); + path.add( new SelfEdge( root ) ); + starts.add( path ); + } - bfsIterate( view, Collections.singletonList( path ), traversal, pass ); + bfsIterate( view, starts, traversal, pass ); } private void bfsIterate( final GraphView view, final List>> thisLayer, final ProjectNetTraversal traversal, @@ -434,21 +644,25 @@ private void bfsIterate( final GraphView view, final List edge : edges ) { - final List> nextPath = new ArrayList>( path ); - - // FIXME: How do we avoid cycle traversal here?? - nextPath.add( edge ); - // call traverseEdge no matter what, to allow traversal to "see" all relationships. - if ( /*( edge instanceof SelfEdge ) ||*/traversal.traverseEdge( edge, nextPath, pass ) ) + 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 ( traversal.isStopped() ) + { + return; + } } } } @@ -473,7 +687,7 @@ private List> getSortedOutEdges( final GraphView view, fi RelationshipUtils.filterTerminalParents( unsorted ); - final List> sorted = new ArrayList>( imposeSelections( view, unsorted ) ); + final List> sorted = new ArrayList>( imposeSelections( view, unsorted, false ) ); Collections.sort( sorted, new RelationshipComparator() ); return sorted; @@ -522,64 +736,27 @@ public ProjectRelationship selectTarget( final SingleVersion } - // @Override - // public EGraphDriver newInstanceFrom( final EProjectNet net, final ProjectRelationshipFilter filter, - // final ProjectVersionRef... from ) - // throws GraphDriverException - // { - // final JungEGraphDriver neo = new JungEGraphDriver( this, filter, net, null, from ); - // neo.restrictProjectMembership( Arrays.asList( from ) ); - // - // return neo; - // } - // - // @Override - // public EGraphDriver newInstance( final EGraphSession workspace, final EProjectNet net, - // final ProjectRelationshipFilter filter, final ProjectVersionRef... from ) - // throws GraphDriverException - // { - // final JungEGraphDriver neo = new JungEGraphDriver( this, filter, net, null, from ); - // neo.restrictProjectMembership( Arrays.asList( from ) ); - // - // return neo; - // } - @Override public boolean containsProject( final GraphView view, final ProjectVersionRef ref ) { - return graph.containsVertex( ref.asProjectVersionRef() ) && !incompleteSubgraphs.contains( ref.asProjectVersionRef() ); + final Set singleton = new HashSet<>(); + singleton.add( ref ); + + final Set visibleProjects = findVisibleProjects( getRoots( view, false ), view.getFilter(), view, singleton ); + return !visibleProjects.isEmpty() && !incompleteSubgraphs.contains( ref.asProjectVersionRef() ); } @Override public boolean containsRelationship( final GraphView view, final ProjectRelationship rel ) { - return graph.containsEdge( rel ); - } - - public void restrictProjectMembership( final Collection refs ) - { - final Set> rels = new HashSet>(); - for ( final ProjectVersionRef ref : refs ) - { - final Collection> edges = graph.getOutEdges( ref.asProjectVersionRef() ); - if ( edges != null ) - { - rels.addAll( edges ); - } - } + final Map pathStates = getPathStates( view ); - restrictRelationshipMembership( rels ); - } + findPathsToRelationships( getRoots( view, false ), view.getFilter(), view, true, pathStates, rel ); - public void restrictRelationshipMembership( final Collection> rels ) - { - graph = new DirectedSparseMultigraph>(); - incompleteSubgraphs.clear(); - variableSubgraphs.clear(); - - addRelationships( rels.toArray( new ProjectRelationship[] {} ) ); + final PathState state = pathStates.get( rel.getTarget() + .asProjectVersionRef() ); - recomputeIncompleteSubgraphs(); + return state != null && state.containsVisibleRelationship( rel ); } @Override @@ -589,42 +766,136 @@ public void close() // NOP; stored in memory. } - // @Override - // public boolean isDerivedFrom( final EGraphDriver driver ) - // { - // return false; - // } - + // FIXME: This is incredibly expensive! @Override public boolean isMissing( final GraphView view, final ProjectVersionRef project ) { - return !graph.containsVertex( project.asProjectVersionRef() ); + return getMissingProjects( view ).contains( project.asProjectVersionRef() ); } + // FIXME: This is incredibly expensive! @Override public boolean hasMissingProjects( final GraphView view ) { - return !incompleteSubgraphs.isEmpty(); + return !getMissingProjects( view ).isEmpty(); } + // FIXME: This is incredibly expensive! @Override public Set getMissingProjects( final GraphView view ) { - final Set result = new HashSet( incompleteSubgraphs ); + return findVisibleProjects( getRoots( view, true ), view.getFilter(), view, incompleteSubgraphs ); + } + + private Set getRoots( final GraphView view, final boolean incomplete ) + { + Set roots = view.getRoots(); + if ( roots == null || roots.isEmpty() ) + { + roots = new HashSet<>( graph.getVertices() ); + if ( !incomplete ) + { + roots.removeAll( incompleteSubgraphs ); + } + } + + return roots; + } + + // FIXME: tighter synchronization + private synchronized Set findVisibleProjects( final Set roots, final ProjectRelationshipFilter filter, + final GraphView view, final Collection originalRefs ) + { + final Map pathStates = getPathStates( view ); + final Set found = new HashSet<>(); + final Set refs = new HashSet<>( originalRefs ); + for ( final Iterator iterator = refs.iterator(); iterator.hasNext(); ) + { + final ProjectVersionRef ref = iterator.next(); + final PathState state = pathStates.get( ref ); + if ( state != null ) + { + if ( state.isVisible() ) + { + found.add( ref ); + } + + iterator.remove(); + } + } + + findPathsToProjects( roots, filter, view, true, pathStates, refs ); + + for ( final ProjectVersionRef ref : refs ) + { + final PathState state = pathStates.get( ref ); + if ( state != null ) + { + if ( state.isVisible() ) + { + found.add( ref ); + } + } + } + + for ( final ProjectVersionRef ref : refs ) + { + if ( roots.contains( ref ) ) + { + found.add( ref ); + } + } + // logger.info( "Got %d missing projects: %s", result.size(), result ); - return result; + return found; + } + + private Map getPathStates( final GraphView view ) + { + return getPathStates( view, true ); + } + + private Map getPathStates( final GraphView view, final boolean create ) + { + @SuppressWarnings( "unchecked" ) + Map pathStates = (Map) getViewCache( view, PATHS ); + if ( pathStates == null && create ) + { + pathStates = new HashMap<>(); + setViewCache( view, PATHS, pathStates ); + } + + return pathStates; + } + + private void setViewCache( final GraphView view, final String key, final Object value ) + { + Map viewCaches = caches.get( view ); + if ( viewCaches == null ) + { + viewCaches = new HashMap<>(); + caches.put( view, viewCaches ); + } + + viewCaches.put( key, value ); + } + + private Object getViewCache( final GraphView view, final String key ) + { + final Map viewCaches = caches.get( view ); + return viewCaches == null ? null : viewCaches.get( key ); } @Override public boolean hasVariableProjects( final GraphView view ) { - return !variableSubgraphs.isEmpty(); + return !getVariableProjects( view ).isEmpty(); } @Override public Set getVariableProjects( final GraphView view ) { - return new HashSet( variableSubgraphs ); + return findVisibleProjects( getRoots( view, false ), view.getFilter(), view, variableSubgraphs ); } @Override @@ -699,19 +970,6 @@ public boolean isCycleParticipant( final GraphView view, final ProjectVersionRef return false; } - @Override - public void recomputeIncompleteSubgraphs() - { - for ( final ProjectVersionRef vertex : getAllProjects( GraphView.GLOBAL ) ) - { - final Collection> outEdges = getRelationshipsDeclaredBy( GraphView.GLOBAL, vertex ); - if ( outEdges != null && !outEdges.isEmpty() ) - { - incompleteSubgraphs.remove( vertex ); - } - } - } - @Override public Map getMetadata( final ProjectVersionRef ref ) { @@ -806,171 +1064,6 @@ public Set getProjectsWithMetadata( final GraphView view, fin return metadataOwners.get( key ); } - // public void selectVersionFor( final ProjectVersionRef variable, final ProjectVersionRef select ) - // throws GraphDriverException - // { - // if ( !select.isSpecificVersion() ) - // { - // throw new GraphDriverException( "Cannot select non-concrete version! Attempted to select: %s", select ); - // } - // - // if ( variable.isSpecificVersion() ) - // { - // throw new GraphDriverException( - // "Cannot select version if target is already a concrete version! Attempted to select for: %s", - // variable ); - // } - // - // selected.put( variable, select ); - // - // // Don't worry about selecting for outbound edges, as those subgraphs are supposed to be the same... - // final Collection> rels = graph.getInEdges( variable ); - // for ( final ProjectRelationship rel : rels ) - // { - // - // ProjectRelationship repl; - // if ( rel.getTarget() - // .asProjectVersionRef() - // .equals( variable ) ) - // { - // repl = rel.selectTarget( (SingleVersion) select.getVersionSpec() ); - // } - // else - // { - // continue; - // } - // - // graph.removeEdge( rel ); - // graph.addEdge( repl, repl.getDeclaring(), repl.getTarget() - // .asProjectVersionRef() ); - // - // replaced.put( rel, repl ); - // } - // } - // - // public Map clearSelectedVersions() - // { - // final Map selected = - // new HashMap( this.selected ); - // - // selected.clear(); - // - // for ( final Map.Entry, ProjectRelationship> entry : replaced.entrySet() ) - // { - // final ProjectRelationship rel = entry.getKey(); - // final ProjectRelationship repl = entry.getValue(); - // - // graph.removeEdge( repl ); - // graph.addEdge( rel, rel.getDeclaring(), rel.getTarget() - // .asProjectVersionRef() ); - // } - // - // for ( final ProjectVersionRef select : new HashSet( selected.values() ) ) - // { - // final Collection> edges = graph.getInEdges( select ); - // if ( edges.isEmpty() ) - // { - // graph.removeVertex( select ); - // } - // } - // - // return selected; - // } - // - // public Map getSelectedVersions() - // { - // return selected; - // } - - private static final class CycleDetectionTraversal - extends AbstractTraversal - { - private final List cycles = new ArrayList(); - - private final ProjectRelationship rel; - - private CycleDetectionTraversal( final ProjectRelationship rel ) - { - this.rel = rel; - } - - public List getCycles() - { - return cycles; - } - - @Override - public boolean preCheck( final ProjectRelationship relationship, final List> path, final int pass ) - { - if ( rel.getDeclaring() - .equals( rel.getTarget() - .asProjectVersionRef() ) ) - { - return false; - } - - new Logger( getClass() ).info( "Checking for cycle:\n\n%s\n\n", join( path, "\n" ) ); - - final ProjectVersionRef from = rel.getDeclaring(); - if ( from.equals( relationship.getTarget() - .asProjectVersionRef() ) ) - { - final List> cycle = new ArrayList>( path ); - cycle.add( rel ); - - cycles.add( new EProjectCycle( cycle ) ); - return false; - } - - return true; - } - } - - private static final class PathDetectionTraversal - extends AbstractTraversal - { - // private final Logger logger = new Logger( getClass() ); - - private final ProjectVersionRef[] to; - - private final Set>> paths = new HashSet>>(); - - private PathDetectionTraversal( final ProjectVersionRef[] refs ) - { - this.to = new ProjectVersionRef[refs.length]; - for ( int i = 0; i < refs.length; i++ ) - { - this.to[i] = refs[i].asProjectVersionRef(); - } - } - - public Set>> getPaths() - { - return paths; - } - - @Override - public boolean preCheck( final ProjectRelationship relationship, final List> path, final int pass ) - { - final ProjectVersionRef target = relationship.getTarget() - .asProjectVersionRef(); - - // logger.info( "Checking path: %s to see if target: %s is in endpoints: %s", join( path, "," ), target, join( to, ", " ) ); - boolean found = false; - for ( final ProjectVersionRef t : to ) - { - if ( t.equals( target ) ) - { - paths.add( new ArrayList>( path ) ); - // logger.info( "+= %s", join( path, ", " ) ); - found = true; - } - } - - return !found; - } - } - @Override public void addDisconnectedProject( final ProjectVersionRef ref ) { diff --git a/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/PathMappingTraversal.java b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/PathMappingTraversal.java new file mode 100644 index 00000000..609359c9 --- /dev/null +++ b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/PathMappingTraversal.java @@ -0,0 +1,56 @@ +package org.commonjava.maven.atlas.graph.spi.jung; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.commonjava.maven.atlas.graph.filter.ProjectRelationshipFilter; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.graph.traverse.AbstractFilteringTraversal; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; + +public class PathMappingTraversal + extends AbstractFilteringTraversal +{ + + private final Map pathStates; + + public PathMappingTraversal( final ProjectRelationshipFilter filter, final Map pathStates ) + { + super( filter ); + this.pathStates = pathStates; + } + + @Override + protected boolean shouldTraverseEdge( final ProjectRelationship relationship, final List> path, final int pass ) + { + return true; + } + + @Override + public boolean preCheck( final ProjectRelationship relationship, final List> path, final int pass ) + { + final List> realPath = new ArrayList<>( path ); + realPath.add( relationship ); + + final ProjectVersionRef target = relationship.getTarget() + .asProjectVersionRef(); + PathState state = pathStates.get( target ); + if ( state == null ) + { + state = new PathState( target ); + pathStates.put( target, state ); + } + + final ProjectRelationshipFilter filter = constructFilter( path ); + if ( filter != null && !filter.accept( relationship ) ) + { + state.addInvisiblePath( realPath, filter ); + return false; + } + + state.addVisiblePath( realPath, filter ); + return true; + } + +} diff --git a/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/PathState.java b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/PathState.java new file mode 100644 index 00000000..729bfe79 --- /dev/null +++ b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/PathState.java @@ -0,0 +1,137 @@ +package org.commonjava.maven.atlas.graph.spi.jung; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.commonjava.maven.atlas.graph.filter.ProjectRelationshipFilter; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; + +public class PathState +{ + + private final ProjectVersionRef ref; + + private final Map>, ProjectRelationshipFilter> visibleFiltersByPath = new LinkedHashMap<>(); + + private final Map>, ProjectRelationshipFilter> invisibleFiltersByPath = new LinkedHashMap<>(); + + public PathState( final ProjectVersionRef ref ) + { + this.ref = ref; + } + + public ProjectVersionRef getRef() + { + return ref; + } + + public boolean isVisible() + { + return !visibleFiltersByPath.isEmpty(); + } + + public boolean isInvisible() + { + return !invisibleFiltersByPath.isEmpty(); + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ( ( ref == null ) ? 0 : ref.hashCode() ); + return result; + } + + @Override + public boolean equals( final Object obj ) + { + if ( this == obj ) + { + return true; + } + if ( obj == null ) + { + return false; + } + if ( getClass() != obj.getClass() ) + { + return false; + } + final PathState other = (PathState) obj; + if ( ref == null ) + { + if ( other.ref != null ) + { + return false; + } + } + else if ( !ref.equals( other.ref ) ) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return String.format( "PathState [%s]", ref ); + } + + public void addVisiblePath( final List> path, final ProjectRelationshipFilter filter ) + { + visibleFiltersByPath.put( path, filter ); + } + + public void addInvisiblePath( final List> path, final ProjectRelationshipFilter filter ) + { + invisibleFiltersByPath.put( path, filter ); + } + + public boolean containsVisibleRelationship( final ProjectRelationship rel ) + { + + for ( final List> path : visibleFiltersByPath.keySet() ) + { + if ( path.contains( rel ) ) + { + return true; + } + } + + return false; + } + + public Collection>> getVisiblePaths() + { + return visibleFiltersByPath.keySet(); + } + + public Collection> getVisibleTargetingRelationships() + { + final Set> result = new HashSet<>( visibleFiltersByPath.size() ); + + for ( final List> path : visibleFiltersByPath.keySet() ) + { + if ( !path.isEmpty() ) + { + result.add( path.get( path.size() - 1 ) ); + } + } + + return result; + } + + public Collection getVisiblePathFilters() + { + return visibleFiltersByPath.values(); + } + +} diff --git a/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/SingleCycleDetectionTraversal.java b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/SingleCycleDetectionTraversal.java new file mode 100644 index 00000000..3ea8a6e2 --- /dev/null +++ b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/SingleCycleDetectionTraversal.java @@ -0,0 +1,81 @@ +package org.commonjava.maven.atlas.graph.spi.jung; + +import java.util.ArrayList; +import java.util.List; + +import org.commonjava.maven.atlas.graph.model.EProjectCycle; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.graph.traverse.AbstractTraversal; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; +import org.commonjava.util.logging.Logger; + +final class SingleCycleDetectionTraversal + extends AbstractTraversal +{ + private final Logger logger = new Logger( getClass() ); + + private final List cycles = new ArrayList(); + + private final ProjectRelationship rel; + + public SingleCycleDetectionTraversal( final ProjectRelationship rel ) + { + this.rel = rel; + if ( rel.getDeclaring() + .equals( rel.getTarget() + .asProjectVersionRef() ) ) + { + final List> cycle = new ArrayList>(); + cycle.add( rel ); + + logger.info( "Found cycle: %s", cycle ); + + cycles.add( new EProjectCycle( cycle ) ); + stop(); + } + } + + public List getCycles() + { + return cycles; + } + + @Override + public boolean preCheck( final ProjectRelationship relationship, final List> path, final int pass ) + { + final ProjectRelationship rootRel; + if ( path.size() > 0 ) + { + rootRel = path.get( 0 ); + } + else + { + rootRel = relationship; + } + + final ProjectVersionRef root = rootRel.getDeclaring(); + final ProjectVersionRef lastTarget = relationship.getTarget() + .asProjectVersionRef(); + + final ProjectVersionRef relDecl = rel.getDeclaring(); + + final ProjectVersionRef relTarget = rel.getTarget() + .asProjectVersionRef(); + + if ( relDecl.equals( lastTarget ) && root.equals( relTarget ) ) + { + final List> cycle = new ArrayList>( path ); + cycle.add( relationship ); + cycle.add( rel ); + + logger.info( "Found cycle: %s", cycle ); + + cycles.add( new EProjectCycle( cycle ) ); + stop(); + + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/drivers/neo4j-embedded/src/main/java/org/commonjava/maven/atlas/graph/spi/neo4j/AbstractNeo4JEGraphDriver.java b/drivers/neo4j-embedded/src/main/java/org/commonjava/maven/atlas/graph/spi/neo4j/AbstractNeo4JEGraphDriver.java index d286d705..66325279 100644 --- a/drivers/neo4j-embedded/src/main/java/org/commonjava/maven/atlas/graph/spi/neo4j/AbstractNeo4JEGraphDriver.java +++ b/drivers/neo4j-embedded/src/main/java/org/commonjava/maven/atlas/graph/spi/neo4j/AbstractNeo4JEGraphDriver.java @@ -64,7 +64,6 @@ import org.commonjava.maven.atlas.graph.filter.ProjectRelationshipFilter; import org.commonjava.maven.atlas.graph.model.EProjectCycle; -import org.commonjava.maven.atlas.graph.model.EProjectNet; import org.commonjava.maven.atlas.graph.model.GraphView; import org.commonjava.maven.atlas.graph.rel.ParentRelationship; import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; @@ -778,15 +777,34 @@ private Set getAllProjectNodes( final GraphView view ) } @Override - public void traverse( final GraphView view, final ProjectNetTraversal traversal, final EProjectNet net, final ProjectVersionRef root ) + public void traverse( final GraphView view, final ProjectNetTraversal traversal, final ProjectVersionRef... roots ) throws GraphDriverException { printCaller( "TRAVERSE" ); - final Node rootNode = getNode( root ); - if ( rootNode == null ) + Node[] rootNodes; + if ( roots.length > 0 ) { - // logger.debug( "Root node not found! (root: %s)", root ); + rootNodes = new Node[roots.length]; + for ( int i = 0; i < roots.length; i++ ) + { + rootNodes[i] = getNode( roots[i] ); + } + } + else if ( view.getRoots() != null && !view.getRoots() + .isEmpty() ) + { + rootNodes = new Node[view.getRoots() + .size()]; + int i = 0; + for ( final ProjectVersionRef root : view.getRoots() ) + { + rootNodes[i++] = getNode( root ); + } + } + else + { + logger.error( "Cannot traverse; no roots provided!" ); return; } @@ -814,7 +832,7 @@ public void traverse( final GraphView view, final ProjectNetTraversal traversal, } // logger.debug( "starting traverse of: %s", net ); - traversal.startTraverse( i, net ); + traversal.startTraverse( i ); final Set rootIds = getRootIds( view ); @@ -824,7 +842,7 @@ public void traverse( final GraphView view, final ProjectNetTraversal traversal, description = description.expand( checker ) .evaluator( checker ); - final Traverser traverser = description.traverse( rootNode ); + final Traverser traverser = description.traverse( rootNodes ); for ( final Path path : traverser ) { // logger.debug( "traversing: %s", path ); @@ -844,7 +862,7 @@ public void traverse( final GraphView view, final ProjectNetTraversal traversal, } } - traversal.endTraverse( i, net ); + traversal.endTraverse( i ); checker.printStats(); } @@ -1335,12 +1353,6 @@ public boolean isCycleParticipant( final GraphView view, final ProjectVersionRef return false; } - @Override - public void recomputeIncompleteSubgraphs() - { - // NOP, handled automatically. - } - @Override public Map getMetadata( final ProjectVersionRef ref ) { diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/AndFilter.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/AndFilter.java index 5cdebbec..18509960 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/AndFilter.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/AndFilter.java @@ -35,6 +35,7 @@ public AndFilter( final T... filters ) super( filters ); } + @Override public boolean accept( final ProjectRelationship rel ) { boolean accepted = true; @@ -56,6 +57,7 @@ protected AbstractAggregatingFilter newChildFilter( final List filters = getFilters(); @@ -67,6 +69,11 @@ public void render( final StringBuilder sb ) boolean first = true; for ( final ProjectRelationshipFilter filter : filters ) { + if ( filter == null ) + { + continue; + } + if ( first ) { first = false; diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/OrFilter.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/OrFilter.java index b9dd9082..5be3b7ce 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/OrFilter.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/OrFilter.java @@ -37,11 +37,17 @@ public OrFilter( final T... filters ) super( filters ); } + @Override public boolean accept( final ProjectRelationship rel ) { boolean accepted = false; for ( final ProjectRelationshipFilter filter : getFilters() ) { + if ( filter == null ) + { + continue; + } + accepted = accepted || filter.accept( rel ); if ( accepted ) { @@ -59,6 +65,7 @@ protected AbstractAggregatingFilter newChildFilter( final List filters = getFilters(); diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectCycle.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectCycle.java index 6ef0dc43..283f8810 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectCycle.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectCycle.java @@ -22,6 +22,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -159,11 +161,13 @@ public int indexOf( final ProjectVersionRef ref ) return targetIdx; } + @Override public Iterator> iterator() { return participants.iterator(); } + @Override public Collection> getAllRelationships() { final Collection> rels = getExactAllRelationships(); @@ -172,6 +176,7 @@ public Collection> getAllRelationships() return rels; } + @Override public Collection> getExactAllRelationships() { return new ArrayList>( participants ); @@ -199,7 +204,18 @@ public String toString() @Override public int hashCode() { - final Set> cycle = new HashSet>( this.participants ); + // FIXME: Horrible hack! This has terrible performance for a hashcode call. + final List> cycle = new ArrayList>( this.participants ); + Collections.sort( cycle, new Comparator>() + { + @Override + public int compare( final ProjectRelationship f, final ProjectRelationship s ) + { + return String.valueOf( f ) + .compareTo( String.valueOf( s ) ); + } + } ); + final int prime = 31; int result = 1; result = prime * result + ( ( cycle == null ) ? 0 : cycle.hashCode() ); diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectGraph.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectGraph.java index 7754246b..f3b83a5d 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectGraph.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectGraph.java @@ -544,12 +544,6 @@ public > Set addAll( final Collection rel .addRelationships( rels.toArray( new ProjectRelationship[] {} ) ); result.removeAll( rejected ); - if ( !result.isEmpty() ) - { - view.getDatabase() - .recomputeIncompleteSubgraphs(); - } - return result; } @@ -568,7 +562,7 @@ protected void traverse( final ProjectVersionRef ref, final ProjectNetTraversal throws GraphDriverException { view.getDatabase() - .traverse( view, traversal, this, ref ); + .traverse( view, traversal, ref ); } @Override diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectWeb.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectWeb.java index 535e251a..32a944bb 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectWeb.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/EProjectWeb.java @@ -137,12 +137,6 @@ public > Set addAll( final Collection rel .addRelationships( rels.toArray( new ProjectRelationship[] {} ) ); result.removeAll( rejected ); - if ( !result.isEmpty() ) - { - view.getDatabase() - .recomputeIncompleteSubgraphs(); - } - return result; } @@ -164,9 +158,6 @@ public > Set addAll( final T... rels ) } } - view.getDatabase() - .recomputeIncompleteSubgraphs(); - return result; } @@ -190,7 +181,7 @@ public void traverse( final ProjectVersionRef start, final ProjectNetTraversal t throws GraphDriverException { view.getDatabase() - .traverse( view, traversal, this, start ); + .traverse( view, traversal, start ); } public Set> getUserRelationships( final ProjectVersionRef ref ) diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/rel/AbstractProjectRelationship.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/rel/AbstractProjectRelationship.java index 6f161e3c..f34740f5 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/rel/AbstractProjectRelationship.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/rel/AbstractProjectRelationship.java @@ -57,55 +57,50 @@ public abstract class AbstractProjectRelationship private boolean cloneUsesLocation = true; - protected AbstractProjectRelationship( final URI source, final RelationshipType type, - final ProjectVersionRef declaring, final T target, final int index ) + protected AbstractProjectRelationship( final URI source, final RelationshipType type, final ProjectVersionRef declaring, final T target, + final int index ) { this( Collections.singleton( source ), POM_ROOT_URI, type, declaring, target, index, false ); } - protected AbstractProjectRelationship( final Collection sources, final RelationshipType type, - final ProjectVersionRef declaring, final T target, final int index ) + protected AbstractProjectRelationship( final Collection sources, final RelationshipType type, final ProjectVersionRef declaring, + final T target, final int index ) { this( sources, POM_ROOT_URI, type, declaring, target, index, false ); } - protected AbstractProjectRelationship( final URI source, final RelationshipType type, - final ProjectVersionRef declaring, final T target, final int index, - final boolean managed ) + protected AbstractProjectRelationship( final URI source, final RelationshipType type, final ProjectVersionRef declaring, final T target, + final int index, final boolean managed ) { this( Collections.singleton( source ), POM_ROOT_URI, type, declaring, target, index, managed ); } - protected AbstractProjectRelationship( final Collection sources, final RelationshipType type, - final ProjectVersionRef declaring, final T target, final int index, - final boolean managed ) + protected AbstractProjectRelationship( final Collection sources, final RelationshipType type, final ProjectVersionRef declaring, + final T target, final int index, final boolean managed ) { this( sources, POM_ROOT_URI, type, declaring, target, index, managed ); } - protected AbstractProjectRelationship( final URI source, final URI pomLocation, final RelationshipType type, - final ProjectVersionRef declaring, final T target, final int index ) + protected AbstractProjectRelationship( final URI source, final URI pomLocation, final RelationshipType type, final ProjectVersionRef declaring, + final T target, final int index ) { this( Collections.singleton( source ), pomLocation, type, declaring, target, index, false ); } - protected AbstractProjectRelationship( final Collection sources, final URI pomLocation, - final RelationshipType type, final ProjectVersionRef declaring, - final T target, final int index ) + protected AbstractProjectRelationship( final Collection sources, final URI pomLocation, final RelationshipType type, + final ProjectVersionRef declaring, final T target, final int index ) { this( sources, pomLocation, type, declaring, target, index, false ); } - protected AbstractProjectRelationship( final URI source, final URI pomLocation, final RelationshipType type, - final ProjectVersionRef declaring, final T target, final int index, - final boolean managed ) + protected AbstractProjectRelationship( final URI source, final URI pomLocation, final RelationshipType type, final ProjectVersionRef declaring, + final T target, final int index, final boolean managed ) { this( Collections.singleton( source ), pomLocation, type, declaring, target, index, managed ); } - protected AbstractProjectRelationship( final Collection sources, final URI pomLocation, - final RelationshipType type, final ProjectVersionRef declaring, - final T target, final int index, final boolean managed ) + protected AbstractProjectRelationship( final Collection sources, final URI pomLocation, final RelationshipType type, + final ProjectVersionRef declaring, final T target, final int index, final boolean managed ) { if ( sources == null ) { @@ -120,8 +115,7 @@ protected AbstractProjectRelationship( final Collection sources, final URI this.pomLocation = pomLocation; if ( declaring == null || target == null ) { - throw new NullPointerException( "Neither declaring ref (" + declaring + ") nor target ref (" + target - + ") can be null!" ); + throw new NullPointerException( "Neither declaring ref (" + declaring + ") nor target ref (" + target + ") can be null!" ); } this.type = type; @@ -172,8 +166,7 @@ public synchronized ProjectRelationship cloneFor( final ProjectVersionRef pro { try { - cloneCtor = - getClass().getConstructor( URI.class, URI.class, ProjectVersionRef.class, target.getClass() ); + cloneCtor = getClass().getConstructor( URI.class, URI.class, ProjectVersionRef.class, target.getClass() ); } catch ( final NoSuchMethodException e ) { @@ -184,9 +177,9 @@ public synchronized ProjectRelationship cloneFor( final ProjectVersionRef pro } catch ( final NoSuchMethodException e2 ) { - throw new IllegalArgumentException( "Missing constructor: " + getClass().getName() - + "(VersionedProjectRef declaring, " + target.getClass() - .getName() + " target)", e2 ); + throw new IllegalArgumentException( "Missing constructor: " + getClass().getName() + "(VersionedProjectRef declaring, " + + target.getClass() + .getName() + " target)", e2 ); } } } @@ -202,19 +195,19 @@ public synchronized ProjectRelationship cloneFor( final ProjectVersionRef pro } catch ( final InstantiationException e ) { - throw new IllegalArgumentException( "Failed to create clone of: " + getClass().getName() + " for project: " - + projectRef + ": " + e.getMessage(), e ); + throw new IllegalArgumentException( "Failed to create clone of: " + getClass().getName() + " for project: " + projectRef + ": " + + e.getMessage(), e ); } catch ( final IllegalAccessException e ) { - throw new IllegalArgumentException( "Failed to create clone of: " + getClass().getName() + " for project: " - + projectRef + ": " + e.getMessage(), e ); + throw new IllegalArgumentException( "Failed to create clone of: " + getClass().getName() + " for project: " + projectRef + ": " + + e.getMessage(), e ); } catch ( final InvocationTargetException e ) { - throw new IllegalArgumentException( "Failed to create clone of: " + getClass().getName() + " for project: " - + projectRef + ": " + e.getTargetException() - .getMessage(), e.getTargetException() ); + throw new IllegalArgumentException( "Failed to create clone of: " + getClass().getName() + " for project: " + projectRef + ": " + + e.getTargetException() + .getMessage(), e.getTargetException() ); } } @@ -226,6 +219,8 @@ public int hashCode() result = prime * result + ( ( declaring == null ) ? 0 : declaring.hashCode() ); result = prime * result + ( ( target == null ) ? 0 : target.hashCode() ); result = prime * result + ( ( type == null ) ? 0 : type.hashCode() ); + result = prime * result + Boolean.valueOf( managed ) + .hashCode(); return result; } @@ -271,6 +266,10 @@ else if ( !target.equals( other.target ) ) { return false; } + if ( managed != other.managed ) + { + return false; + } return true; } diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/spi/GraphDatabaseDriver.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/spi/GraphDatabaseDriver.java index 3a387dd9..f6f73dcc 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/spi/GraphDatabaseDriver.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/spi/GraphDatabaseDriver.java @@ -23,7 +23,6 @@ import java.util.Set; import org.commonjava.maven.atlas.graph.model.EProjectCycle; -import org.commonjava.maven.atlas.graph.model.EProjectNet; import org.commonjava.maven.atlas.graph.model.GraphView; import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; import org.commonjava.maven.atlas.graph.rel.RelationshipType; @@ -58,9 +57,6 @@ public interface GraphDatabaseDriver boolean clearSelectedVersions(); - void recomputeIncompleteSubgraphs() - throws GraphDriverException; - void reindex() throws GraphDriverException; @@ -88,7 +84,7 @@ void reindex() Set getAllProjects( GraphView view ); - void traverse( GraphView view, ProjectNetTraversal traversal, EProjectNet net, ProjectVersionRef root ) + void traverse( GraphView view, ProjectNetTraversal traversal, ProjectVersionRef... root ) throws GraphDriverException; boolean containsProject( GraphView view, ProjectVersionRef ref ); diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/AbstractFilteringTraversal.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/AbstractFilteringTraversal.java index 4d662942..5aa0a6c0 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/AbstractFilteringTraversal.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/AbstractFilteringTraversal.java @@ -49,18 +49,15 @@ protected AbstractFilteringTraversal( final ProjectRelationshipFilter filter, fi rootFilter = filter; } - protected AbstractFilteringTraversal( final ProjectRelationshipFilter filter, final int passes, - final TraversalType... types ) + protected AbstractFilteringTraversal( final ProjectRelationshipFilter filter, final int passes, final TraversalType... types ) { super( passes, types ); rootFilter = filter; } - protected abstract boolean shouldTraverseEdge( ProjectRelationship relationship, - List> path, int pass ); + protected abstract boolean shouldTraverseEdge( ProjectRelationship relationship, List> path, int pass ); - protected void edgeTraversalFinished( final ProjectRelationship relationship, - final List> path, final int pass ) + protected void edgeTraversalFinished( final ProjectRelationship relationship, final List> path, final int pass ) { } @@ -70,15 +67,13 @@ public final ProjectRelationshipFilter getRootFilter() } @Override - public final void edgeTraversed( final ProjectRelationship relationship, - final List> path, final int pass ) + public final void edgeTraversed( final ProjectRelationship relationship, final List> path, final int pass ) { edgeTraversalFinished( relationship, path, pass ); } @Override - public final boolean traverseEdge( final ProjectRelationship relationship, - final List> path, final int pass ) + public final boolean traverseEdge( final ProjectRelationship relationship, final List> path, final int pass ) { if ( !preCheck( relationship, path, pass ) ) { @@ -93,8 +88,7 @@ public final boolean traverseEdge( final ProjectRelationship relationship, } @Override - public boolean preCheck( final ProjectRelationship relationship, final List> path, - final int pass ) + public boolean preCheck( final ProjectRelationship relationship, final List> path, final int pass ) { boolean result = true; if ( seen.contains( relationship ) ) @@ -112,7 +106,7 @@ public boolean preCheck( final ProjectRelationship relationship, final List

> path ) + protected ProjectRelationshipFilter constructFilter( final List> path ) { if ( rootFilter == null ) { diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/AbstractTraversal.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/AbstractTraversal.java index b5916d27..c270b917 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/AbstractTraversal.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/AbstractTraversal.java @@ -20,7 +20,6 @@ import java.util.Collections; import java.util.List; -import org.commonjava.maven.atlas.graph.model.EProjectNet; import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; import org.commonjava.maven.atlas.graph.spi.GraphDriverException; @@ -32,6 +31,8 @@ public abstract class AbstractTraversal private final int passes; + private boolean stop = false; + protected AbstractTraversal() { this.passes = 1; @@ -50,6 +51,18 @@ protected AbstractTraversal( final TraversalType... types ) this.types = Arrays.asList( types ); } + protected void stop() + { + stop = true; + } + + @Override + public boolean isStopped() + { + return stop; + } + + @Override public TraversalType getType( final int pass ) { if ( types == null || types.isEmpty() ) @@ -64,32 +77,36 @@ else if ( pass >= types.size() ) return types.get( pass ); } - public void startTraverse( final int pass, final EProjectNet network ) + @Override + public void startTraverse( final int pass ) throws GraphDriverException { } + @Override public int getRequiredPasses() { return passes; } - public void endTraverse( final int pass, final EProjectNet network ) + @Override + public void endTraverse( final int pass ) throws GraphDriverException { } - public void edgeTraversed( final ProjectRelationship relationship, final List> path, - final int pass ) + @Override + public void edgeTraversed( final ProjectRelationship relationship, final List> path, final int pass ) { } - public boolean traverseEdge( final ProjectRelationship relationship, final List> path, - final int pass ) + @Override + public boolean traverseEdge( final ProjectRelationship relationship, final List> path, final int pass ) { return preCheck( relationship, path, pass ); } + @Override public TraversalType[] getTraversalTypes() { return types.toArray( new TraversalType[] {} ); diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/BuildOrderTraversal.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/BuildOrderTraversal.java index 80af8275..f5841378 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/BuildOrderTraversal.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/BuildOrderTraversal.java @@ -51,14 +51,48 @@ public BuildOrderTraversal( final ProjectRelationshipFilter filter ) super( new OrFilter( filter, new ParentFilter( false ) ) ); } - public BuildOrder getBuildOrder() + public BuildOrder getBuildOrder( final EProjectNet network ) { + detectCycles( network ); return new BuildOrder( order, cycles ); } + private void detectCycles( final EProjectNet network ) + { + Set cycles = network.getCycles(); + if ( cycles != null ) + { + cycles = new HashSet( cycles ); + for ( final Iterator iterator = cycles.iterator(); iterator.hasNext(); ) + { + final EProjectCycle eProjectCycle = iterator.next(); + ProjectRelationshipFilter filter = getRootFilter(); + + boolean include = true; + for ( final ProjectRelationship rel : eProjectCycle ) + { + if ( !filter.accept( rel ) ) + { + include = false; + break; + } + + filter = filter.getChildFilter( rel ); + } + + if ( !include ) + { + iterator.remove(); + } + } + + } + + this.cycles = cycles; + } + @Override - protected boolean shouldTraverseEdge( final ProjectRelationship relationship, - final List> path, final int pass ) + protected boolean shouldTraverseEdge( final ProjectRelationship relationship, final List> path, final int pass ) { final ProjectVersionRef decl = relationship.getDeclaring(); @@ -88,41 +122,10 @@ protected boolean shouldTraverseEdge( final ProjectRelationship relationship, } @Override - public void endTraverse( final int pass, final EProjectNet network ) + public void endTraverse( final int pass ) throws GraphDriverException { - super.endTraverse( pass, network ); - - Set cycles = network.getCycles(); - if ( cycles != null ) - { - cycles = new HashSet( cycles ); - for ( final Iterator iterator = cycles.iterator(); iterator.hasNext(); ) - { - final EProjectCycle eProjectCycle = iterator.next(); - ProjectRelationshipFilter filter = getRootFilter(); - - boolean include = true; - for ( final ProjectRelationship rel : eProjectCycle ) - { - if ( !filter.accept( rel ) ) - { - include = false; - break; - } - - filter = filter.getChildFilter( rel ); - } - - if ( !include ) - { - iterator.remove(); - } - } - - } - - this.cycles = cycles; + super.endTraverse( pass ); } } diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/ProjectNetTraversal.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/ProjectNetTraversal.java index d7100c73..038dcfe1 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/ProjectNetTraversal.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/ProjectNetTraversal.java @@ -18,7 +18,6 @@ import java.util.List; -import org.commonjava.maven.atlas.graph.model.EProjectNet; import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; import org.commonjava.maven.atlas.graph.spi.GraphDriverException; @@ -29,10 +28,10 @@ public interface ProjectNetTraversal int getRequiredPasses(); - void startTraverse( int pass, EProjectNet network ) + void startTraverse( int pass ) throws GraphDriverException; - void endTraverse( int pass, EProjectNet network ) + void endTraverse( int pass ) throws GraphDriverException; boolean traverseEdge( ProjectRelationship relationship, List> path, int pass ); @@ -41,5 +40,7 @@ void endTraverse( int pass, EProjectNet network ) boolean preCheck( ProjectRelationship relationship, List> path, int pass ); + boolean isStopped(); + TraversalType[] getTraversalTypes(); } diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/print/StructurePrintingTraversal.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/print/StructurePrintingTraversal.java index 446b4573..d860b3c8 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/print/StructurePrintingTraversal.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/traverse/print/StructurePrintingTraversal.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Map; -import org.commonjava.maven.atlas.graph.model.EProjectNet; import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; import org.commonjava.maven.atlas.graph.spi.GraphDriverException; import org.commonjava.maven.atlas.graph.traverse.ProjectNetTraversal; @@ -38,8 +37,7 @@ public class StructurePrintingTraversal private final StructureRelationshipPrinter relationshipPrinter; - private final Map>> outboundLinks = - new HashMap>>(); + private final Map>> outboundLinks = new HashMap>>(); public StructurePrintingTraversal() { @@ -59,16 +57,14 @@ public StructurePrintingTraversal( final StructureRelationshipPrinter relationsh this.relationshipPrinter = relationshipPrinter; } - public StructurePrintingTraversal( final ProjectNetTraversal traversal, - final StructureRelationshipPrinter relationshipPrinter ) + public StructurePrintingTraversal( final ProjectNetTraversal traversal, final StructureRelationshipPrinter relationshipPrinter ) { this.traversal = traversal; this.relationshipPrinter = relationshipPrinter; } @Override - public boolean traverseEdge( final ProjectRelationship relationship, final List> path, - final int pass ) + public boolean traverseEdge( final ProjectRelationship relationship, final List> path, final int pass ) { if ( traversal == null || traversal.traverseEdge( relationship, path, pass ) ) { @@ -100,8 +96,7 @@ public String printStructure( final ProjectVersionRef from, final String indent return printStructure( from, null, null, indent ); } - public String printStructure( final ProjectVersionRef from, final String header, final String footer, - final String indent ) + public String printStructure( final ProjectVersionRef from, final String header, final String footer, final String indent ) { // final Set> refs = new HashSet>(); // for ( final Map.Entry>> entry : outboundLinks.entrySet() ) @@ -136,8 +131,7 @@ public String printStructure( final ProjectVersionRef from, final String header, return builder.toString(); } - private void printLinks( final ProjectVersionRef from, final StringBuilder builder, final String indent, - final int depth ) + private void printLinks( final ProjectVersionRef from, final StringBuilder builder, final String indent, final int depth ) { final List> outbound = outboundLinks.get( from ); if ( outbound != null ) @@ -164,35 +158,33 @@ private void printLinks( final ProjectVersionRef from, final StringBuilder build } @Override - public boolean preCheck( final ProjectRelationship relationship, final List> path, - final int pass ) + public boolean preCheck( final ProjectRelationship relationship, final List> path, final int pass ) { return traversal == null || traversal.preCheck( relationship, path, pass ); } @Override - public void startTraverse( final int pass, final EProjectNet network ) + public void startTraverse( final int pass ) throws GraphDriverException { if ( traversal != null ) { - traversal.startTraverse( pass, network ); + traversal.startTraverse( pass ); } } @Override - public void endTraverse( final int pass, final EProjectNet network ) + public void endTraverse( final int pass ) throws GraphDriverException { if ( traversal != null ) { - traversal.endTraverse( pass, network ); + traversal.endTraverse( pass ); } } @Override - public void edgeTraversed( final ProjectRelationship relationship, final List> path, - final int pass ) + public void edgeTraversed( final ProjectRelationship relationship, final List> path, final int pass ) { if ( traversal != null ) { @@ -218,4 +210,10 @@ public TraversalType[] getTraversalTypes() return traversal == null ? new TraversalType[] { TraversalType.depth_first } : traversal.getTraversalTypes(); } + @Override + public boolean isStopped() + { + return false; + } + } diff --git a/tck/src/main/java/org/commonjava/maven/atlas/tck/graph/CycleDetectionTCK.java b/tck/src/main/java/org/commonjava/maven/atlas/tck/graph/CycleDetectionTCK.java index d9f5c8ff..e38d361d 100644 --- a/tck/src/main/java/org/commonjava/maven/atlas/tck/graph/CycleDetectionTCK.java +++ b/tck/src/main/java/org/commonjava/maven/atlas/tck/graph/CycleDetectionTCK.java @@ -39,7 +39,6 @@ public abstract class CycleDetectionTCK { @Test - // @Ignore public void introducesCycleCheckWithExistingGraph() throws Exception { diff --git a/tck/src/main/java/org/commonjava/maven/atlas/tck/graph/traverse/BuildOrderTraversalTCK.java b/tck/src/main/java/org/commonjava/maven/atlas/tck/graph/traverse/BuildOrderTraversalTCK.java index d069ea7c..f1384273 100644 --- a/tck/src/main/java/org/commonjava/maven/atlas/tck/graph/traverse/BuildOrderTraversalTCK.java +++ b/tck/src/main/java/org/commonjava/maven/atlas/tck/graph/traverse/BuildOrderTraversalTCK.java @@ -77,7 +77,7 @@ public void simpleDependencyBuildOrder() final BuildOrderTraversal bo = new BuildOrderTraversal( new DependencyFilter( DependencyScope.test ) ); graph.traverse( bo ); - final BuildOrder buildOrderObj = bo.getBuildOrder(); + final BuildOrder buildOrderObj = bo.getBuildOrder( graph ); final List buildOrder = buildOrderObj.getOrder(); assertRelativeOrder( relativeOrder, buildOrder ); @@ -122,7 +122,7 @@ public void simpleDependencyBuildOrder_includeDepParent() final BuildOrderTraversal bo = new BuildOrderTraversal( new DependencyFilter( DependencyScope.test ) ); graph.traverse( bo ); - final BuildOrder buildOrderObj = bo.getBuildOrder(); + final BuildOrder buildOrderObj = bo.getBuildOrder( graph ); final List buildOrder = buildOrderObj.getOrder(); new Logger( getClass() ).info( "Build order: %s", buildOrder ); @@ -171,7 +171,7 @@ public void simpleDependencyBuildOrder_IgnorePluginPath() final BuildOrderTraversal bo = new BuildOrderTraversal( new DependencyFilter( DependencyScope.test ) ); graph.traverse( bo ); - final BuildOrder buildOrderObj = bo.getBuildOrder(); + final BuildOrder buildOrderObj = bo.getBuildOrder( graph ); final List buildOrder = buildOrderObj.getOrder(); assertRelativeOrder( relativeOrder, buildOrder ); @@ -221,7 +221,7 @@ public void simpleDependencyBuildOrder_runtimeDepsOnly() final BuildOrderTraversal bo = new BuildOrderTraversal( new DependencyFilter( DependencyScope.runtime ) ); graph.traverse( bo ); - final BuildOrder buildOrderObj = bo.getBuildOrder(); + final BuildOrder buildOrderObj = bo.getBuildOrder( graph ); final List buildOrder = buildOrderObj.getOrder(); assertThat( buildOrder.size(), equalTo( 3 ) ); @@ -267,7 +267,7 @@ public void simpleDependencyBuildOrder_ignoreExcluded() final BuildOrderTraversal bo = new BuildOrderTraversal( new DependencyFilter( DependencyScope.runtime ) ); graph.traverse( bo ); - final BuildOrder buildOrderObj = bo.getBuildOrder(); + final BuildOrder buildOrderObj = bo.getBuildOrder( graph ); final List buildOrder = buildOrderObj.getOrder(); logger.info( "Build order: %s", buildOrder ); @@ -318,7 +318,7 @@ public void simpleEverythingBuildOrder() final BuildOrderTraversal bo = new BuildOrderTraversal(); graph.traverse( bo ); - final BuildOrder buildOrderObj = bo.getBuildOrder(); + final BuildOrder buildOrderObj = bo.getBuildOrder( graph ); final List buildOrder = buildOrderObj.getOrder(); System.out.printf( "Build order: %s\n", buildOrder ); From 1de54b959b86cae9ef3808c1fcaf56f1146923a7 Mon Sep 17 00:00:00 2001 From: John Casey Date: Sat, 2 Nov 2013 00:10:09 -0500 Subject: [PATCH 2/2] very early stages of implementing bare-metal driver and decommissioning jung driver. --- drivers/baremetal/pom.xml | 38 ++ .../maven/atlas/graph/bare/AtlasGraphDB.java | 359 ++++++++++++++++++ .../maven/atlas/graph/bare/AtlasPath.java | 87 +++++ .../atlas/graph/bare/BareMetalDriver.java | 299 +++++++++++++++ .../atlas/graph/bare/GraphVisibility.java | 93 +++++ .../graph/spi/jung/CycleDetectionTest.java | 49 +++ .../graph/spi/jung/EProjectGraphTest.java | 49 +++ .../graph/spi/jung/GraphWorkspaceTest.java | 33 ++ .../graph/spi/jung/SubGraphSelectionTest.java | 49 +++ .../jung/traverse/AncestryTraversalTest.java | 49 +++ .../traverse/BuildOrderTraversalTest.java | 49 +++ .../TransitiveDependencyTraversalTest.java | 49 +++ drivers/jung/pom.xml | 4 + .../spi/jung/CycleDetectionTraversal.java | 80 ++-- .../graph/spi/jung/JungEGraphDriver.java | 353 +++-------------- drivers/pom.xml | 1 + .../maven/atlas/ident/ref/ArtifactRef.java | 4 + .../maven/atlas/graph/filter/BOMFilter.java | 37 ++ .../maven/atlas/graph/model/GraphView.java | 68 ++++ .../graph/rel/ExtensionRelationship.java | 12 +- .../atlas/graph/util/GraphEdgeSelector.java | 14 + .../maven/atlas/graph/util/SelfEdge.java | 52 +++ .../maven/atlas/graph/util/Traversals.java | 199 ++++++++++ 23 files changed, 1695 insertions(+), 332 deletions(-) create mode 100644 drivers/baremetal/pom.xml create mode 100644 drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/AtlasGraphDB.java create mode 100644 drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/AtlasPath.java create mode 100644 drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/BareMetalDriver.java create mode 100644 drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/GraphVisibility.java create mode 100644 drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTest.java create mode 100644 drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/EProjectGraphTest.java create mode 100644 drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/GraphWorkspaceTest.java create mode 100644 drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/SubGraphSelectionTest.java create mode 100644 drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/AncestryTraversalTest.java create mode 100644 drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/BuildOrderTraversalTest.java create mode 100644 drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/TransitiveDependencyTraversalTest.java create mode 100644 relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/BOMFilter.java create mode 100644 relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/GraphEdgeSelector.java create mode 100644 relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/SelfEdge.java create mode 100644 relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/Traversals.java diff --git a/drivers/baremetal/pom.xml b/drivers/baremetal/pom.xml new file mode 100644 index 00000000..7c0dd390 --- /dev/null +++ b/drivers/baremetal/pom.xml @@ -0,0 +1,38 @@ + + + + 4.0.0 + + + org.commonjava.maven.atlas + atlas-drivers-parent + 0.9.2-SNAPSHOT + + + atlas-driver-baremetal + + Atlas :: Maven Project-Graph :: Bare-Metal Driver + + + + commons-codec + commons-codec + + + + diff --git a/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/AtlasGraphDB.java b/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/AtlasGraphDB.java new file mode 100644 index 00000000..7e6450df --- /dev/null +++ b/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/AtlasGraphDB.java @@ -0,0 +1,359 @@ +package org.commonjava.maven.atlas.graph.bare; + +import static org.apache.commons.codec.digest.DigestUtils.shaHex; +import static org.apache.commons.lang.StringUtils.join; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.WeakHashMap; + +import org.commonjava.maven.atlas.graph.model.GraphView; +import org.commonjava.maven.atlas.graph.rel.DependencyRelationship; +import org.commonjava.maven.atlas.graph.rel.ExtensionRelationship; +import org.commonjava.maven.atlas.graph.rel.ParentRelationship; +import org.commonjava.maven.atlas.graph.rel.PluginDependencyRelationship; +import org.commonjava.maven.atlas.graph.rel.PluginRelationship; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.ident.ref.ArtifactRef; +import org.commonjava.maven.atlas.ident.ref.ProjectRef; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; +import org.commonjava.maven.atlas.ident.version.InvalidVersionSpecificationException; +import org.commonjava.util.logging.Logger; + +public class AtlasGraphDB +{ + + private final Logger logger = new Logger( getClass() ); + + private final Map>> inEdges = new HashMap<>(); + + private final Map>> outEdges = new HashMap<>(); + + private final Map canonicalGAVs = new HashMap<>(); + + private final Map canonicalArtifacts = new HashMap<>(); + + private final Map canonicalGAs = new HashMap<>(); + + private final Map, ProjectRelationship> canonicalRelationships = new HashMap<>(); + + private final Set incomplete = new HashSet<>(); + + private final Set variable = new HashSet<>(); + + public ProjectRelationship add( ProjectRelationship rel ) + { + final ProjectVersionRef target = normalizeGAV( rel.getTarget() + .asProjectVersionRef() ); + try + { + if ( !target.getVersionSpec() + .isSingle() ) + { + logger.info( "Adding variable target: %s", target ); + variable.add( target ); + } + } + catch ( final InvalidVersionSpecificationException e ) + { + logger.error( "Failed to determine variable-version status of: %s. Reason: %s", e, target, e.getMessage() ); + return null; + } + + rel = normalize( rel ); + + if ( !outEdges.containsKey( target ) ) + { + logger.info( "Adding incomplete target: %s", target ); + incomplete.add( target ); + } + + final ProjectVersionRef declaring = rel.getDeclaring(); + + addEdge( rel, declaring, outEdges ); + addEdge( rel, target, inEdges ); + + logger.info( "removing from incomplete status: %s", declaring ); + incomplete.remove( declaring ); + + final Set paths = getPathsEndingWith( declaring ); + if ( paths != null ) + { + for ( final AtlasPath path : paths ) + { + extendPath( path, rel ); + } + } + + return rel; + } + + private void addEdge( final ProjectRelationship rel, final ProjectVersionRef ref, + final Map>> edges ) + { + SortedSet> set = edges.get( ref ); + if ( set == null ) + { + set = new TreeSet<>(); + edges.put( ref, set ); + } + + set.add( rel ); + } + + public ProjectRelationship normalize( final ProjectRelationship rel ) + { + ProjectRelationship result = canonicalRelationships.get( rel ); + if ( result == null ) + { + final ProjectVersionRef d = normalizeGAV( rel.getDeclaring() ); + + switch ( rel.getType() ) + { + case DEPENDENCY: + { + final ArtifactRef t = normalizeArtifact( rel.getTargetArtifact() ); + final DependencyRelationship dr = (DependencyRelationship) rel; + result = + new DependencyRelationship( rel.getSources(), rel.getPomLocation(), d, t, dr.getScope(), rel.getIndex(), rel.isManaged(), + normalizeAll( dr.getExcludes() ) ); + break; + } + case EXTENSION: + { + final ProjectVersionRef t = normalizeGAV( rel.getTarget() ); + result = new ExtensionRelationship( rel.getSources(), rel.getPomLocation(), d, t, rel.getIndex() ); + break; + } + case PARENT: + { + final ProjectVersionRef t = normalizeGAV( rel.getTarget() ); + result = new ParentRelationship( rel.getSources(), d, t ); + break; + } + case PLUGIN: + { + final ProjectVersionRef t = normalizeGAV( rel.getTarget() ); + result = + new PluginRelationship( rel.getSources(), rel.getPomLocation(), d, t, rel.getIndex(), rel.isManaged(), + ( (PluginRelationship) rel ).isReporting() ); + break; + } + case PLUGIN_DEP: + { + final ArtifactRef t = normalizeArtifact( rel.getTargetArtifact() ); + final ProjectRef plugin = normalizeGA( ( (PluginDependencyRelationship) rel ).getPlugin() ); + result = new PluginDependencyRelationship( rel.getSources(), rel.getPomLocation(), d, plugin, t, rel.getIndex(), rel.isManaged() ); + break; + } + } + + canonicalRelationships.put( result, result ); + } + else + { + result.addSources( rel.getSources() ); + } + + return result; + } + + public ProjectRef[] normalizeAll( final Collection refs ) + { + final ProjectRef[] result = new ProjectRef[refs.size()]; + int i = 0; + for ( final ProjectRef ref : refs ) + { + result[i++] = normalizeGA( ref ); + } + + return result; + } + + public ProjectRef normalizeGA( final ProjectRef ref ) + { + ProjectRef result = canonicalGAs.get( ref ); + if ( result == null ) + { + result = ref; + canonicalGAs.put( ref, ref ); + } + + return result; + } + + public ProjectVersionRef normalizeGAV( final ProjectVersionRef ref ) + { + ProjectVersionRef result = canonicalGAVs.get( ref ); + if ( result == null ) + { + result = ref; + canonicalGAs.put( ref, ref ); + } + + normalizeGA( ref.asProjectRef() ); + + return result; + } + + public ArtifactRef normalizeArtifact( final ArtifactRef ref ) + { + ArtifactRef result = canonicalArtifacts.get( ref ); + if ( result == null ) + { + result = ref; + canonicalGAs.put( ref, ref ); + } + + normalizeGAV( result.asProjectVersionRef() ); + normalizeGA( result.asProjectRef() ); + + return result; + } + + private final Map pathsById = new HashMap<>(); + + private final Map> pathStartGAV = new HashMap<>(); + + private final Map> pathEndGAV = new HashMap<>(); + + private final Map> pathsContainingGAV = new HashMap<>(); + + private final Map, Set> pathsContainingRelationship = new HashMap<>(); + + private final Map visibility = new WeakHashMap<>(); + + // private AtlasPath getPath( final ProjectRelationship... path ) + // { + // final String id = shaHex( join( path, ">" ) ); + // AtlasPath result = pathsById.get( id ); + // if ( result == null ) + // { + // result = new AtlasPath( id, path ); + // storePath( result ); + // } + // + // return result; + // } + // + private AtlasPath getPath( final List> path ) + { + final String id = shaHex( join( path, ">" ) ); + AtlasPath result = pathsById.get( id ); + if ( result == null ) + { + result = new AtlasPath( id, path ); + storePath( result ); + } + + return result; + } + + private void storePath( final AtlasPath path ) + { + pathsById.put( path.getId(), path ); + boolean first = true; + for ( final Iterator> it = path.iterator(); it.hasNext(); ) + { + final ProjectRelationship r = it.next(); + + final ProjectVersionRef d = r.getDeclaring(); + mapPathToProject( d, path, null ); + + final ProjectVersionRef t = r.getTarget() + .asProjectVersionRef(); + mapPathToProject( t, path, null ); + + if ( first ) + { + first = false; + mapPathToProject( d, path, Boolean.TRUE ); + } + + if ( !it.hasNext() ) + { + mapPathToProject( t, path, Boolean.FALSE ); + } + + Set pathIds = pathsContainingRelationship.get( r ); + if ( pathIds == null ) + { + pathIds = new HashSet<>(); + pathsContainingRelationship.put( r, pathIds ); + } + + pathIds.add( path.getId() ); + } + } + + private void mapPathToProject( final ProjectVersionRef ref, final AtlasPath path, final Boolean endpointMarker ) + { + Map> map; + if ( endpointMarker == null ) + { + map = pathsContainingGAV; + } + else if ( endpointMarker ) + { + map = pathStartGAV; + } + else + { + map = pathEndGAV; + } + + Set pathIds = map.get( ref ); + if ( pathIds == null ) + { + pathIds = new HashSet<>(); + map.put( ref, pathIds ); + } + + pathIds.add( path.getId() ); + } + + private Set getPathsEndingWith( final ProjectVersionRef ref ) + { + final Set ids = pathEndGAV.get( ref ); + if ( ids == null ) + { + return null; + } + + final Set paths = new HashSet<>(); + for ( final String id : ids ) + { + paths.add( pathsById.get( id ) ); + } + + return paths; + } + + // private AtlasPath getPreviousPath( final AtlasPath path ) + // { + // return getPath( path.getPreviousParts() ); + // } + + private AtlasPath extendPath( final AtlasPath path, final ProjectRelationship next ) + { + final List> nextList = new ArrayList<>( path.getPathElements() ); + nextList.add( next ); + final AtlasPath result = getPath( nextList ); + + for ( final GraphVisibility vis : visibility.values() ) + { + vis.addPathIfVisible( result ); + } + + return result; + } + +} diff --git a/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/AtlasPath.java b/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/AtlasPath.java new file mode 100644 index 00000000..d91144cc --- /dev/null +++ b/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/AtlasPath.java @@ -0,0 +1,87 @@ +package org.commonjava.maven.atlas.graph.bare; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; + +// FIXME: Replace static storage of instances with a factory wrapper of some sort. +public class AtlasPath + implements Iterable> +{ + + private final String id; + + private final List> path; + + AtlasPath( final String id, final ProjectRelationship... path ) + { + this.id = id; + this.path = Collections.unmodifiableList( Arrays.asList( path ) ); + } + + AtlasPath( final String id, final List> path ) + { + this.id = id; + this.path = Collections.unmodifiableList( path ); + } + + public String getId() + { + return id; + } + + public List> getPathElements() + { + return path; + } + + @Override + public Iterator> iterator() + { + return path.iterator(); + } + + public List> getPreviousParts() + { + return path.size() > 1 ? path.subList( 0, path.size() - 1 ) : Collections.> emptyList(); + } + + public ProjectRelationship getCurrentPart() + { + return path.isEmpty() ? null : path.get( path.size() - 1 ); + } + + public ProjectVersionRef getStartGAV() + { + return path.isEmpty() ? null : path.get( 0 ) + .getDeclaring(); + } + + public ProjectVersionRef getEndGAV() + { + return path.isEmpty() ? null : path.get( path.size() - 1 ) + .getTarget() + .asProjectVersionRef(); + } + + public boolean contains( final ProjectVersionRef ref ) + { + for ( final ProjectRelationship rel : path ) + { + if ( rel.getDeclaring() + .equals( ref ) || rel.getTarget() + .asProjectVersionRef() + .equals( ref ) ) + { + return true; + } + } + + return false; + } + +} diff --git a/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/BareMetalDriver.java b/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/BareMetalDriver.java new file mode 100644 index 00000000..62b6670c --- /dev/null +++ b/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/BareMetalDriver.java @@ -0,0 +1,299 @@ +package org.commonjava.maven.atlas.graph.bare; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.commonjava.maven.atlas.graph.model.EProjectCycle; +import org.commonjava.maven.atlas.graph.model.GraphView; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.graph.rel.RelationshipType; +import org.commonjava.maven.atlas.graph.spi.GraphDatabaseDriver; +import org.commonjava.maven.atlas.graph.spi.GraphDriverException; +import org.commonjava.maven.atlas.graph.traverse.ProjectNetTraversal; +import org.commonjava.maven.atlas.ident.ref.ProjectRef; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; + +public class BareMetalDriver + implements GraphDatabaseDriver +{ + + private final AtlasGraphDB graph; + + public BareMetalDriver() + { + this.graph = new AtlasGraphDB(); + } + + @Override + public void close() + throws IOException + { + } + + @Override + public boolean addCycle( final EProjectCycle cycle ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public void addDisconnectedProject( final ProjectVersionRef ref ) + { + // TODO Auto-generated method stub + + } + + @Override + public void addMetadata( final ProjectVersionRef ref, final String key, final String value ) + { + // TODO Auto-generated method stub + + } + + @Override + public void setMetadata( final ProjectVersionRef ref, final Map metadata ) + { + // TODO Auto-generated method stub + + } + + @Override + public Set> addRelationships( final ProjectRelationship... rel ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean clearSelectedVersions() + { + // TODO Auto-generated method stub + return false; + } + + @Override + public void reindex() + throws GraphDriverException + { + // TODO Auto-generated method stub + + } + + @Override + public void selectVersionFor( final ProjectVersionRef ref, final ProjectVersionRef selected ) + { + // TODO Auto-generated method stub + + } + + @Override + public void selectVersionForAll( final ProjectRef ref, final ProjectVersionRef selected ) + { + // TODO Auto-generated method stub + + } + + @Override + public Collection> getRelationshipsDeclaredBy( final GraphView view, final ProjectVersionRef root ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Collection> getRelationshipsTargeting( final GraphView view, final ProjectVersionRef root ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Collection> getAllRelationships( final GraphView view ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set>> getAllPathsTo( final GraphView view, final ProjectVersionRef... projectVersionRefs ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean introducesCycle( final GraphView view, final ProjectRelationship rel ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public Set getAllProjects( final GraphView view ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public void traverse( final GraphView view, final ProjectNetTraversal traversal, final ProjectVersionRef... root ) + throws GraphDriverException + { + // TODO Auto-generated method stub + + } + + @Override + public boolean containsProject( final GraphView view, final ProjectVersionRef ref ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean containsRelationship( final GraphView view, final ProjectRelationship rel ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isMissing( final GraphView view, final ProjectVersionRef project ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean hasMissingProjects( final GraphView view ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public Set getMissingProjects( final GraphView view ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean hasVariableProjects( final GraphView view ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public Set getVariableProjects( final GraphView view ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set getCycles( final GraphView view ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isCycleParticipant( final GraphView view, final ProjectRelationship rel ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isCycleParticipant( final GraphView view, final ProjectVersionRef ref ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public Map getMetadata( final ProjectVersionRef ref ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getMetadata( final ProjectVersionRef ref, final Set keys ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set getProjectsWithMetadata( final GraphView view, final String key ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set> getDirectRelationshipsFrom( final GraphView view, final ProjectVersionRef from, + final boolean includeManagedInfo, final RelationshipType... types ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set> getDirectRelationshipsTo( final GraphView view, final ProjectVersionRef to, final boolean includeManagedInfo, + final RelationshipType... types ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set getProjectsMatching( final ProjectRef projectRef, final GraphView view ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public ProjectVersionRef getSelectedFor( final ProjectVersionRef ref ) + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getSelections() + { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getWildcardSelections() + { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean hasSelectionFor( final ProjectVersionRef ref ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean hasSelectionForAll( final ProjectRef ref ) + { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/GraphVisibility.java b/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/GraphVisibility.java new file mode 100644 index 00000000..78cee333 --- /dev/null +++ b/drivers/baremetal/src/main/java/org/commonjava/maven/atlas/graph/bare/GraphVisibility.java @@ -0,0 +1,93 @@ +package org.commonjava.maven.atlas.graph.bare; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.commonjava.maven.atlas.graph.filter.ProjectRelationshipFilter; +import org.commonjava.maven.atlas.graph.model.GraphView; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; + +public class GraphVisibility +{ + + private final GraphView view; + + private final Set> includedRelationships = new HashSet<>(); + + private final Set includedProjects = new HashSet<>(); + + private final Map> pathFilters = new HashMap<>(); + + public GraphVisibility( final GraphView view ) + { + this.view = view; + if ( view.getRoots() != null ) + { + includedProjects.addAll( view.getRoots() ); + } + } + + public boolean addPathExtensionIfVisible( final ProjectRelationship last, final Set knownParents ) + { + // if the path is rooted outside this view's roots, reject it immediately + final Set roots = view.getRoots(); + if ( roots != null && !roots.contains( path.getStartGAV() ) ) + { + return false; + } + + final List> previousParts = path.getPreviousParts(); + ProjectRelationship previous = null; + if ( previousParts != null && !previousParts.isEmpty() ) + { + previous = previousParts.get( previousParts.size() - 1 ); + } + + if ( previous != null ) + { + if ( !includedRelationships.contains( previous ) ) + { + return false; + } + + final Set filters = pathFilters.get( previous ); + Set lastFilters = null; + if ( filters != null && !filters.isEmpty() ) + { + for ( final ProjectRelationshipFilter filter : filters ) + { + if ( filter.accept( last ) ) + { + final ProjectRelationshipFilter lastFilter = filter.getChildFilter( last ); + + if ( lastFilter != null ) + { + if ( lastFilters == null ) + { + lastFilters = new HashSet<>(); + pathFilters.put( last, lastFilters ); + } + + lastFilters.add( lastFilter ); + } + } + } + + if ( lastFilters != null && !lastFilters.isEmpty() ) + { + includedRelationships.add( last ); + includedProjects.add( last.getDeclaring() ); + includedProjects.add( last.getTarget() + .asProjectVersionRef() ); + + return true; + } + } + } + } + +} diff --git a/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTest.java b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTest.java new file mode 100644 index 00000000..1c77a34d --- /dev/null +++ b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTest.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (C) 2013 John Casey. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ******************************************************************************/ +package org.commonjava.maven.atlas.graph.spi.jung; + +import org.apache.log4j.Level; +import org.commonjava.maven.atlas.graph.EGraphManager; +import org.commonjava.maven.atlas.graph.bare.JungWorkspaceFactory; +import org.commonjava.maven.atlas.tck.graph.CycleDetectionTCK; +import org.commonjava.util.logging.Log4jUtil; +import org.junit.BeforeClass; + +public class CycleDetectionTest + extends CycleDetectionTCK +{ + @BeforeClass + public static void logging() + { + Log4jUtil.configure( Level.DEBUG ); + } + + private EGraphManager manager; + + @Override + protected EGraphManager getManager() + throws Exception + { + if ( manager == null ) + { + manager = new EGraphManager( new JungWorkspaceFactory() ); + } + + return manager; + } + +} diff --git a/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/EProjectGraphTest.java b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/EProjectGraphTest.java new file mode 100644 index 00000000..36fc3069 --- /dev/null +++ b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/EProjectGraphTest.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (C) 2013 John Casey. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ******************************************************************************/ +package org.commonjava.maven.atlas.graph.spi.jung; + +import org.apache.log4j.Level; +import org.commonjava.maven.atlas.graph.EGraphManager; +import org.commonjava.maven.atlas.graph.bare.JungWorkspaceFactory; +import org.commonjava.maven.atlas.tck.graph.EProjectGraphTCK; +import org.commonjava.util.logging.Log4jUtil; +import org.junit.BeforeClass; + +public class EProjectGraphTest + extends EProjectGraphTCK +{ + @BeforeClass + public static void logging() + { + Log4jUtil.configure( Level.DEBUG ); + } + + private EGraphManager manager; + + @Override + protected EGraphManager getManager() + throws Exception + { + if ( manager == null ) + { + manager = new EGraphManager( new JungWorkspaceFactory() ); + } + + return manager; + } + +} diff --git a/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/GraphWorkspaceTest.java b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/GraphWorkspaceTest.java new file mode 100644 index 00000000..e525277b --- /dev/null +++ b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/GraphWorkspaceTest.java @@ -0,0 +1,33 @@ +package org.commonjava.maven.atlas.graph.spi.jung; + +import org.apache.log4j.Level; +import org.commonjava.maven.atlas.graph.EGraphManager; +import org.commonjava.maven.atlas.graph.bare.JungWorkspaceFactory; +import org.commonjava.maven.atlas.tck.graph.GraphWorkspaceSPI_TCK; +import org.commonjava.util.logging.Log4jUtil; +import org.junit.BeforeClass; + +public class GraphWorkspaceTest + extends GraphWorkspaceSPI_TCK +{ + @BeforeClass + public static void logging() + { + Log4jUtil.configure( Level.DEBUG ); + } + + private EGraphManager manager; + + @Override + protected EGraphManager getManager() + throws Exception + { + if ( manager == null ) + { + manager = new EGraphManager( new JungWorkspaceFactory() ); + } + + return manager; + } + +} diff --git a/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/SubGraphSelectionTest.java b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/SubGraphSelectionTest.java new file mode 100644 index 00000000..fee5ca02 --- /dev/null +++ b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/SubGraphSelectionTest.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (C) 2013 John Casey. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ******************************************************************************/ +package org.commonjava.maven.atlas.graph.spi.jung; + +import org.apache.log4j.Level; +import org.commonjava.maven.atlas.graph.EGraphManager; +import org.commonjava.maven.atlas.graph.bare.JungWorkspaceFactory; +import org.commonjava.maven.atlas.tck.graph.EProjectGraphTCK; +import org.commonjava.util.logging.Log4jUtil; +import org.junit.BeforeClass; + +public class SubGraphSelectionTest + extends EProjectGraphTCK +{ + @BeforeClass + public static void logging() + { + Log4jUtil.configure( Level.DEBUG ); + } + + private EGraphManager manager; + + @Override + protected EGraphManager getManager() + throws Exception + { + if ( manager == null ) + { + manager = new EGraphManager( new JungWorkspaceFactory() ); + } + + return manager; + } + +} diff --git a/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/AncestryTraversalTest.java b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/AncestryTraversalTest.java new file mode 100644 index 00000000..4dcf7b90 --- /dev/null +++ b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/AncestryTraversalTest.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (C) 2013 John Casey. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ******************************************************************************/ +package org.commonjava.maven.atlas.graph.spi.jung.traverse; + +import org.apache.log4j.Level; +import org.commonjava.maven.atlas.graph.EGraphManager; +import org.commonjava.maven.atlas.graph.bare.JungWorkspaceFactory; +import org.commonjava.maven.atlas.tck.graph.traverse.AncestryTraversalTCK; +import org.commonjava.util.logging.Log4jUtil; +import org.junit.BeforeClass; + +public class AncestryTraversalTest + extends AncestryTraversalTCK +{ + @BeforeClass + public static void logging() + { + Log4jUtil.configure( Level.DEBUG ); + } + + private EGraphManager manager; + + @Override + protected EGraphManager getManager() + throws Exception + { + if ( manager == null ) + { + manager = new EGraphManager( new JungWorkspaceFactory() ); + } + + return manager; + } + +} diff --git a/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/BuildOrderTraversalTest.java b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/BuildOrderTraversalTest.java new file mode 100644 index 00000000..6e014d7d --- /dev/null +++ b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/BuildOrderTraversalTest.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (C) 2013 John Casey. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ******************************************************************************/ +package org.commonjava.maven.atlas.graph.spi.jung.traverse; + +import org.apache.log4j.Level; +import org.commonjava.maven.atlas.graph.EGraphManager; +import org.commonjava.maven.atlas.graph.bare.JungWorkspaceFactory; +import org.commonjava.maven.atlas.tck.graph.traverse.BuildOrderTraversalTCK; +import org.commonjava.util.logging.Log4jUtil; +import org.junit.BeforeClass; + +public class BuildOrderTraversalTest + extends BuildOrderTraversalTCK +{ + @BeforeClass + public static void logging() + { + Log4jUtil.configure( Level.DEBUG ); + } + + private EGraphManager manager; + + @Override + protected EGraphManager getManager() + throws Exception + { + if ( manager == null ) + { + manager = new EGraphManager( new JungWorkspaceFactory() ); + } + + return manager; + } + +} diff --git a/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/TransitiveDependencyTraversalTest.java b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/TransitiveDependencyTraversalTest.java new file mode 100644 index 00000000..e30b9956 --- /dev/null +++ b/drivers/baremetal/src/test/java/org/commonjava/maven/atlas/graph/spi/jung/traverse/TransitiveDependencyTraversalTest.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (C) 2013 John Casey. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ******************************************************************************/ +package org.commonjava.maven.atlas.graph.spi.jung.traverse; + +import org.apache.log4j.Level; +import org.commonjava.maven.atlas.graph.EGraphManager; +import org.commonjava.maven.atlas.graph.bare.JungWorkspaceFactory; +import org.commonjava.maven.atlas.tck.graph.traverse.TransitiveDependencyTraversalTCK; +import org.commonjava.util.logging.Log4jUtil; +import org.junit.BeforeClass; + +public class TransitiveDependencyTraversalTest + extends TransitiveDependencyTraversalTCK +{ + @BeforeClass + public static void logging() + { + Log4jUtil.configure( Level.DEBUG ); + } + + private EGraphManager manager; + + @Override + protected EGraphManager getManager() + throws Exception + { + if ( manager == null ) + { + manager = new EGraphManager( new JungWorkspaceFactory() ); + } + + return manager; + } + +} diff --git a/drivers/jung/pom.xml b/drivers/jung/pom.xml index b2691705..2f7d747c 100644 --- a/drivers/jung/pom.xml +++ b/drivers/jung/pom.xml @@ -33,6 +33,10 @@ net.sf.jung jung-graph-impl + + commons-codec + commons-codec + diff --git a/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTraversal.java b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTraversal.java index f20eb879..1c704df3 100644 --- a/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTraversal.java +++ b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTraversal.java @@ -1,6 +1,8 @@ package org.commonjava.maven.atlas.graph.spi.jung; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -20,6 +22,23 @@ final class CycleDetectionTraversal private final Set> hits = new HashSet<>(); + private final Set> existingCycleParticipants; + + private final Set> rels; + + public CycleDetectionTraversal( final Collection existingCycles, final Set> skipped, + final ProjectRelationship... rels ) + { + this.rels = new HashSet<>( Arrays.asList( rels ) ); + this.rels.removeAll( skipped ); + + existingCycleParticipants = new HashSet<>(); + for ( final EProjectCycle cycle : existingCycles ) + { + existingCycleParticipants.addAll( cycle.getAllRelationships() ); + } + } + public Set> getCycleInjectors() { return hits; @@ -34,32 +53,15 @@ public Set getCycles() public boolean preCheck( final ProjectRelationship relationship, final List> path, final int pass ) { boolean found = false; - int i = 0; - for ( final ProjectRelationship rel : path ) + if ( existingCycleParticipants.contains( relationship ) || hits.contains( relationship ) ) { - final ProjectVersionRef from = rel.getDeclaring(); - - if ( from.equals( relationship.getTarget() - .asProjectVersionRef() ) ) - { - final List> sub = path.subList( i, path.size() ); - final List> cycle = new ArrayList>( sub ); - cycle.add( relationship ); - - logger.info( "Found cycle: %s", cycle ); - - cycles.add( new EProjectCycle( cycle ) ); - hits.add( relationship ); - - found = true; - } - - i++; + logger.info( "%s is part of an existing cycle. Skip traversal.", relationship ); + found = true; } - if ( relationship.getDeclaring() - .equals( relationship.getTarget() - .asProjectVersionRef() ) ) + if ( !found && relationship.getDeclaring() + .equals( relationship.getTarget() + .asProjectVersionRef() ) ) { final List> cycle = new ArrayList>(); cycle.add( relationship ); @@ -72,6 +74,38 @@ public boolean preCheck( final ProjectRelationship relationship, final List

rel : path ) + { + final ProjectVersionRef from = rel.getDeclaring(); + + if ( from.equals( relationship.getTarget() + .asProjectVersionRef() ) ) + { + final List> sub = path.subList( i, path.size() ); + final List> cycle = new ArrayList>( sub ); + cycle.add( relationship ); + + logger.info( "Found cycle: %s", cycle ); + + cycles.add( new EProjectCycle( cycle ) ); + hits.add( relationship ); + + found = true; + } + + i++; + } + } + + if ( hits.containsAll( rels ) ) + { + logger.info( "Accounted for all new relationships. Stopping analysis." ); + stop(); + } + return !found; } } \ No newline at end of file diff --git a/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/JungEGraphDriver.java b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/JungEGraphDriver.java index b7df260b..3d9389ee 100644 --- a/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/JungEGraphDriver.java +++ b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/JungEGraphDriver.java @@ -19,6 +19,7 @@ import static org.apache.commons.lang.StringUtils.join; import static org.commonjava.maven.atlas.graph.util.RelationshipUtils.POM_ROOT_URI; import static org.commonjava.maven.atlas.graph.util.RelationshipUtils.UNKNOWN_SOURCE_URI; +import static org.commonjava.maven.atlas.graph.util.Traversals.dfsTraverse; import java.io.IOException; import java.net.URI; @@ -29,34 +30,30 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import org.apache.commons.lang.StringUtils; +import org.commonjava.maven.atlas.graph.bare.GraphEdgeSelector; import org.commonjava.maven.atlas.graph.filter.OrFilter; import org.commonjava.maven.atlas.graph.filter.ProjectRelationshipFilter; import org.commonjava.maven.atlas.graph.model.EProjectCycle; import org.commonjava.maven.atlas.graph.model.GraphView; -import org.commonjava.maven.atlas.graph.rel.AbstractProjectRelationship; -import org.commonjava.maven.atlas.graph.rel.ParentRelationship; import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; import org.commonjava.maven.atlas.graph.rel.RelationshipComparator; -import org.commonjava.maven.atlas.graph.rel.RelationshipPathComparator; import org.commonjava.maven.atlas.graph.rel.RelationshipType; import org.commonjava.maven.atlas.graph.spi.GraphDatabaseDriver; import org.commonjava.maven.atlas.graph.spi.GraphDriverException; import org.commonjava.maven.atlas.graph.traverse.FilteringTraversal; import org.commonjava.maven.atlas.graph.traverse.ProjectNetTraversal; import org.commonjava.maven.atlas.graph.util.RelationshipUtils; +import org.commonjava.maven.atlas.graph.util.Traversals; import org.commonjava.maven.atlas.graph.workspace.GraphWorkspace; import org.commonjava.maven.atlas.graph.workspace.GraphWorkspaceConfiguration; -import org.commonjava.maven.atlas.ident.ref.ArtifactRef; import org.commonjava.maven.atlas.ident.ref.ProjectRef; import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; -import org.commonjava.maven.atlas.ident.version.InvalidVersionSpecificationException; import org.commonjava.maven.atlas.ident.version.SingleVersion; import org.commonjava.util.logging.Logger; @@ -68,10 +65,12 @@ public class JungEGraphDriver { private static final String PATHS = "paths"; - private final Logger logger = new Logger( getClass() ); + final Logger logger = new Logger( getClass() ); private final DirectedGraph> graph = new DirectedSparseMultigraph<>(); + // private final AtlasGraph graph = new AtlasGraph(); + private final Map> byGA = new HashMap<>(); private transient Map selectedForAll = new HashMap<>(); @@ -142,9 +141,9 @@ public Collection> getAllRelationships( final GraphView v final FilteringTraversal traversal = new FilteringTraversal( view.getFilter(), true ); // FIXME: if the view doesn't have any roots, this will fail. - dfsTraverse( view, traversal, 0, view.getRoots() - .toArray( new ProjectVersionRef[view.getRoots() - .size()] ) ); + dfsTraverse( view, traversal, 0, selector, view.getRoots() + .toArray( new ProjectVersionRef[view.getRoots() + .size()] ) ); return traversal.getCapturedRelationships(); } @@ -252,6 +251,7 @@ public Set> addRelationships( final ProjectRelationship> skipped = new HashSet>(); final Set targets = new HashSet<>( rels.length ); + for ( final ProjectRelationship rel : rels ) { final ProjectVersionRef target = rel.getTarget() @@ -259,64 +259,11 @@ public Set> addRelationships( final ProjectRelationship> edges = new ArrayList>( graph.findEdgeSet( rel.getDeclaring(), target ) ); - if ( !edges.contains( rel ) ) - { - logger.info( "Adding edge: %s -> %s", rel.getDeclaring(), target ); - graph.addEdge( rel, rel.getDeclaring(), target.asProjectVersionRef() ); - } - else - { - final int idx = edges.indexOf( rel ); - final ProjectRelationship existing = edges.get( idx ); - - logger.info( "Adding sources: %s to existing edge: %s", rel.getSources(), existing ); - - existing.addSources( rel.getSources() ); - } - - logger.info( "removing from incomplete status: %s", rel.getDeclaring() ); - incompleteSubgraphs.remove( rel.getDeclaring() ); + graph.add( rel ); } - final CycleDetectionTraversal traversal = new CycleDetectionTraversal(); - dfsTraverse( GraphView.GLOBAL, traversal, 0, targets.toArray( new ProjectVersionRef[targets.size()] ) ); + final CycleDetectionTraversal traversal = new CycleDetectionTraversal( cycles, skipped, rels ); + dfsTraverse( GraphView.GLOBAL, traversal, 0, selector, targets.toArray( new ProjectVersionRef[targets.size()] ) ); logger.info( "Detecting cycles..." ); @@ -416,6 +363,7 @@ private void findPathsToProjects( final Set roots, final Proj if ( reTraverse ) { + logger.info( "Re-traversing to capture path states for: %s", refs ); final PathMappingTraversal traversal = new PathMappingTraversal( filter, pathStates ); if ( roots == null || roots.isEmpty() ) @@ -424,7 +372,7 @@ private void findPathsToProjects( final Set roots, final Proj return; } - dfsTraverse( view, traversal, 0, roots.toArray( new ProjectVersionRef[roots.size()] ) ); + dfsTraverse( view, traversal, 0, selector, roots.toArray( new ProjectVersionRef[roots.size()] ) ); } } @@ -451,7 +399,7 @@ private void findPathsToProjects( final Set roots, final Proj return; } - dfsTraverse( view, traversal, 0, roots.toArray( new ProjectVersionRef[roots.size()] ) ); + dfsTraverse( view, traversal, 0, selector, roots.toArray( new ProjectVersionRef[roots.size()] ) ); } } @@ -480,7 +428,7 @@ private void findPathsToRelationships( final Set roots, final return; } - dfsTraverse( view, traversal, 0, roots.toArray( new ProjectVersionRef[roots.size()] ) ); + dfsTraverse( view, traversal, 0, selector, roots.toArray( new ProjectVersionRef[roots.size()] ) ); } } @@ -489,8 +437,8 @@ public boolean introducesCycle( final GraphView view, final ProjectRelationship< { final SingleCycleDetectionTraversal traversal = new SingleCycleDetectionTraversal( rel ); - dfsTraverse( view, traversal, 0, rel.getTarget() - .asProjectVersionRef() ); + dfsTraverse( view, traversal, 0, selector, rel.getTarget() + .asProjectVersionRef() ); return !traversal.getCycles() .isEmpty(); @@ -502,240 +450,6 @@ public Set getAllProjects( final GraphView view ) return findVisibleProjects( getRoots( view, false ), view.getFilter(), view, graph.getVertices() ); } - @Override - public void traverse( final GraphView view, final ProjectNetTraversal traversal, final ProjectVersionRef... roots ) - throws GraphDriverException - { - ProjectVersionRef[] start; - if ( roots.length > 0 ) - { - start = roots; - } - else if ( view.getRoots() != null && !view.getRoots() - .isEmpty() ) - { - start = view.getRoots() - .toArray( new ProjectVersionRef[view.getRoots() - .size()] ); - } - else - { - logger.error( "Cannot traverse; no roots specified!" ); - return; - } - - for ( int i = 0; i < traversal.getRequiredPasses(); i++ ) - { - traversal.startTraverse( i ); - - switch ( traversal.getType( i ) ) - { - case breadth_first: - { - bfsTraverse( view, traversal, i, start ); - break; - } - case depth_first: - { - dfsTraverse( view, traversal, i, start ); - break; - } - } - - traversal.endTraverse( i ); - } - } - - // TODO: Implement without recursion. - private void dfsTraverse( final GraphView view, final ProjectNetTraversal traversal, final int pass, final ProjectVersionRef... roots ) - { - for ( final ProjectVersionRef root : roots ) - { - dfsIterate( view, root, traversal, new LinkedList>(), pass ); - } - } - - private void dfsIterate( final GraphView view, final ProjectVersionRef node, final ProjectNetTraversal traversal, - final LinkedList> path, final int pass ) - { - final List> edges = getSortedOutEdges( view, node ); - if ( edges != null ) - { - for ( final ProjectRelationship edge : edges ) - { - if ( traversal.traverseEdge( edge, path, pass ) ) - { - - if ( !( edge instanceof ParentRelationship ) || !( (ParentRelationship) edge ).isTerminus() ) - { - path.addLast( edge ); - - final ProjectVersionRef target = edge.getTarget() - .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 ) - { - dfsIterate( view, target, traversal, path, pass ); - } - - path.removeLast(); - } - - traversal.edgeTraversed( edge, path, pass ); - if ( traversal.isStopped() ) - { - return; - } - } - - } - } - } - - // TODO: Implement without recursion. - private void bfsTraverse( final GraphView view, final ProjectNetTraversal traversal, final int pass, final ProjectVersionRef... roots ) - { - final List>> starts = new ArrayList<>(); - for ( final ProjectVersionRef root : roots ) - { - final List> path = new ArrayList>(); - path.add( new SelfEdge( root ) ); - starts.add( path ); - } - - bfsIterate( view, starts, traversal, pass ); - } - - private void bfsIterate( final GraphView view, final List>> thisLayer, final ProjectNetTraversal traversal, - final int pass ) - { - final List>> nextLayer = new ArrayList>>(); - - for ( final List> path : thisLayer ) - { - if ( path.isEmpty() ) - { - continue; - } - - final ProjectVersionRef node = path.get( path.size() - 1 ) - .getTarget() - .asProjectVersionRef(); - - if ( !path.isEmpty() && ( path.get( 0 ) instanceof SelfEdge ) ) - { - path.remove( 0 ); - } - - final List> edges = getSortedOutEdges( view, 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 ( traversal.isStopped() ) - { - return; - } - } - } - } - } - - if ( !nextLayer.isEmpty() ) - { - Collections.sort( nextLayer, new RelationshipPathComparator() ); - bfsIterate( view, nextLayer, traversal, pass ); - } - } - - private List> getSortedOutEdges( final GraphView view, final ProjectVersionRef node ) - { - Collection> unsorted = graph.getOutEdges( node.asProjectVersionRef() ); - if ( unsorted == null ) - { - return null; - } - - unsorted = new ArrayList>( unsorted ); - - RelationshipUtils.filterTerminalParents( unsorted ); - - final List> sorted = new ArrayList>( imposeSelections( view, unsorted, false ) ); - Collections.sort( sorted, new RelationshipComparator() ); - - return sorted; - } - - private static final class SelfEdge - extends AbstractProjectRelationship - { - - private static final long serialVersionUID = 1L; - - SelfEdge( final ProjectVersionRef ref ) - { - super( (URI) null, null, ref.asProjectVersionRef(), ref.asProjectVersionRef(), 0 ); - } - - @Override - public ArtifactRef getTargetArtifact() - { - return getTarget().asPomArtifact(); - } - - @Override - public ProjectRelationship selectDeclaring( final SingleVersion version ) - { - return selectDeclaring( version, false ); - } - - @Override - public ProjectRelationship selectDeclaring( final SingleVersion version, final boolean force ) - { - return new SelfEdge( getDeclaring().selectVersion( version, force ) ); - } - - @Override - public ProjectRelationship selectTarget( final SingleVersion version ) - { - return selectTarget( version, false ); - } - - @Override - public ProjectRelationship selectTarget( final SingleVersion version, final boolean force ) - { - return new SelfEdge( getDeclaring().selectVersion( version, force ) ); - } - - } - @Override public boolean containsProject( final GraphView view, final ProjectVersionRef ref ) { @@ -1187,4 +901,33 @@ public Map getWildcardSelections() return selectedForAll; } + @Override + public void traverse( final GraphView view, final ProjectNetTraversal traversal, final ProjectVersionRef... root ) + throws GraphDriverException + { + Traversals.traverse( view, traversal, selector, root ); + } + + private final GraphEdgeSelector selector = new GraphEdgeSelector() + { + + @Override + public List> getSortedOutEdges( final GraphView view, final ProjectVersionRef node ) + { + Collection> unsorted = graph.getOutEdges( node.asProjectVersionRef() ); + if ( unsorted == null ) + { + return null; + } + + unsorted = new ArrayList>( unsorted ); + + RelationshipUtils.filterTerminalParents( unsorted ); + + final List> sorted = new ArrayList>( imposeSelections( view, unsorted, false ) ); + Collections.sort( sorted, new RelationshipComparator() ); + + return sorted; + } + }; } diff --git a/drivers/pom.xml b/drivers/pom.xml index 85369eb5..77f872c2 100644 --- a/drivers/pom.xml +++ b/drivers/pom.xml @@ -46,6 +46,7 @@ + baremetal jung neo4j-embedded diff --git a/identities/src/main/java/org/commonjava/maven/atlas/ident/ref/ArtifactRef.java b/identities/src/main/java/org/commonjava/maven/atlas/ident/ref/ArtifactRef.java index 88bca88b..e7533b3f 100644 --- a/identities/src/main/java/org/commonjava/maven/atlas/ident/ref/ArtifactRef.java +++ b/identities/src/main/java/org/commonjava/maven/atlas/ident/ref/ArtifactRef.java @@ -144,6 +144,10 @@ else if ( !tc.equals( other.tc ) ) { return false; } + if ( optional != other.optional ) + { + return false; + } return true; } diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/BOMFilter.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/BOMFilter.java new file mode 100644 index 00000000..d19c8384 --- /dev/null +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/filter/BOMFilter.java @@ -0,0 +1,37 @@ +package org.commonjava.maven.atlas.graph.filter; + +import org.commonjava.maven.atlas.graph.rel.DependencyRelationship; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.graph.rel.RelationshipType; +import org.commonjava.maven.atlas.ident.DependencyScope; + +public class BOMFilter + extends AbstractTypedFilter +{ + + public BOMFilter() + { + super( RelationshipType.DEPENDENCY, false, true, false ); + } + + @Override + public ProjectRelationshipFilter getChildFilter( final ProjectRelationship parent ) + { + return null; + } + + @Override + public void render( final StringBuilder sb ) + { + sb.append( "BOMs" ); + } + + @Override + protected boolean doAccept( final ProjectRelationship rel ) + { + final DependencyRelationship dr = (DependencyRelationship) rel; + return "pom".equals( dr.getTarget() + .getType() ) && DependencyScope._import.equals( dr.getScope() ); + } + +} diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/GraphView.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/GraphView.java index c80ff607..6c890ca4 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/GraphView.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/model/GraphView.java @@ -2,7 +2,9 @@ import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.WeakHashMap; @@ -24,6 +26,12 @@ public class GraphView private final WeakHashMap cache = new WeakHashMap(); + private final Map> acceptingFilters = new HashMap<>(); + + private final Map> rejectingFilters = new HashMap<>(); + + private final Map allGAVs = new HashMap<>(); + public GraphView( final GraphWorkspace workspace, final ProjectRelationshipFilter filter, final Collection roots ) { this.filter = filter; @@ -52,6 +60,66 @@ public GraphView( final GraphWorkspace workspace, final ProjectVersionRef... roo this.workspace = workspace; } + public ProjectVersionRef normalize( final ProjectVersionRef ref ) + { + ProjectVersionRef result = allGAVs.get( ref ); + if ( result == null ) + { + allGAVs.put( ref, ref ); + result = ref; + } + + return result; + } + + public boolean addAcceptingFilter( ProjectVersionRef ref, final ProjectRelationshipFilter filter ) + { + ref = normalize( ref ); + synchronized ( ref ) + { + Set filters = acceptingFilters.get( ref ); + if ( filters == null ) + { + filters = new HashSet<>(); + acceptingFilters.put( ref, filters ); + } + + return filters.add( filter ); + } + } + + public Set getAcceptingFilters( final ProjectVersionRef ref ) + { + return acceptingFilters.get( ref ); + } + + public boolean addRejectingFilter( ProjectVersionRef ref, final ProjectRelationshipFilter filter ) + { + ref = normalize( ref ); + synchronized ( ref ) + { + Set filters = rejectingFilters.get( ref ); + if ( filters == null ) + { + filters = new HashSet<>(); + rejectingFilters.put( ref, filters ); + } + + return filters.add( filter ); + } + } + + public Set getRejectingFilters( final ProjectVersionRef ref ) + { + return rejectingFilters.get( ref ); + } + + public void clearGAVFilters() + { + acceptingFilters.clear(); + rejectingFilters.clear(); + } + public ProjectRelationshipFilter getFilter() { return filter; diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/rel/ExtensionRelationship.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/rel/ExtensionRelationship.java index 85a96db5..7f7fcc96 100644 --- a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/rel/ExtensionRelationship.java +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/rel/ExtensionRelationship.java @@ -31,18 +31,22 @@ public final class ExtensionRelationship private static final long serialVersionUID = 1L; - public ExtensionRelationship( final URI source, final ProjectVersionRef declaring, final ProjectVersionRef target, - final int index ) + public ExtensionRelationship( final URI source, final ProjectVersionRef declaring, final ProjectVersionRef target, final int index ) { super( source, RelationshipType.EXTENSION, declaring, target, index ); } - public ExtensionRelationship( final Collection sources, final ProjectVersionRef declaring, - final ProjectVersionRef target, final int index ) + public ExtensionRelationship( final Collection sources, final ProjectVersionRef declaring, final ProjectVersionRef target, final int index ) { super( sources, RelationshipType.EXTENSION, declaring, target, index ); } + public ExtensionRelationship( final Collection sources, final URI pomLocation, final ProjectVersionRef declaring, + final ProjectVersionRef target, final int index ) + { + super( sources, pomLocation, RelationshipType.EXTENSION, declaring, target, index ); + } + @Override public String toString() { diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/GraphEdgeSelector.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/GraphEdgeSelector.java new file mode 100644 index 00000000..7a35b7df --- /dev/null +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/GraphEdgeSelector.java @@ -0,0 +1,14 @@ +package org.commonjava.maven.atlas.graph.util; + +import java.util.List; + +import org.commonjava.maven.atlas.graph.model.GraphView; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; + +public interface GraphEdgeSelector +{ + + List> getSortedOutEdges( GraphView view, ProjectVersionRef node ); + +} diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/SelfEdge.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/SelfEdge.java new file mode 100644 index 00000000..b9923bfd --- /dev/null +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/SelfEdge.java @@ -0,0 +1,52 @@ +package org.commonjava.maven.atlas.graph.util; + +import java.net.URI; + +import org.commonjava.maven.atlas.graph.rel.AbstractProjectRelationship; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.ident.ref.ArtifactRef; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; +import org.commonjava.maven.atlas.ident.version.SingleVersion; + +final class SelfEdge + extends AbstractProjectRelationship +{ + + private static final long serialVersionUID = 1L; + + SelfEdge( final ProjectVersionRef ref ) + { + super( (URI) null, null, ref.asProjectVersionRef(), ref.asProjectVersionRef(), 0 ); + } + + @Override + public ArtifactRef getTargetArtifact() + { + return getTarget().asPomArtifact(); + } + + @Override + public ProjectRelationship selectDeclaring( final SingleVersion version ) + { + return selectDeclaring( version, false ); + } + + @Override + public ProjectRelationship selectDeclaring( final SingleVersion version, final boolean force ) + { + return new SelfEdge( getDeclaring().selectVersion( version, force ) ); + } + + @Override + public ProjectRelationship selectTarget( final SingleVersion version ) + { + return selectTarget( version, false ); + } + + @Override + public ProjectRelationship selectTarget( final SingleVersion version, final boolean force ) + { + return new SelfEdge( getDeclaring().selectVersion( version, force ) ); + } + +} \ No newline at end of file diff --git a/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/Traversals.java b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/Traversals.java new file mode 100644 index 00000000..fed0fcf9 --- /dev/null +++ b/relationships-api/src/main/java/org/commonjava/maven/atlas/graph/util/Traversals.java @@ -0,0 +1,199 @@ +package org.commonjava.maven.atlas.graph.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.commonjava.maven.atlas.graph.model.GraphView; +import org.commonjava.maven.atlas.graph.rel.ParentRelationship; +import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; +import org.commonjava.maven.atlas.graph.rel.RelationshipPathComparator; +import org.commonjava.maven.atlas.graph.spi.GraphDriverException; +import org.commonjava.maven.atlas.graph.traverse.ProjectNetTraversal; +import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; +import org.commonjava.util.logging.Logger; + +public final class Traversals +{ + + private static final Logger logger = new Logger( Traversals.class ); + + private Traversals() + { + } + + public static void traverse( final GraphView view, final ProjectNetTraversal traversal, final GraphEdgeSelector selector, + final ProjectVersionRef... roots ) + throws GraphDriverException + { + ProjectVersionRef[] start; + if ( roots.length > 0 ) + { + start = roots; + } + else if ( view.getRoots() != null && !view.getRoots() + .isEmpty() ) + { + start = view.getRoots() + .toArray( new ProjectVersionRef[view.getRoots() + .size()] ); + } + else + { + logger.error( "Cannot traverse; no roots specified!" ); + return; + } + + for ( int i = 0; i < traversal.getRequiredPasses(); i++ ) + { + traversal.startTraverse( i ); + + switch ( traversal.getType( i ) ) + { + case breadth_first: + { + bfsTraverse( view, traversal, i, selector, start ); + break; + } + case depth_first: + { + dfsTraverse( view, traversal, i, selector, start ); + break; + } + } + + traversal.endTraverse( i ); + } + } + + public static void dfsTraverse( final GraphView view, final ProjectNetTraversal traversal, final int pass, final GraphEdgeSelector selector, + final ProjectVersionRef... roots ) + { + for ( final ProjectVersionRef root : roots ) + { + dfsIterate( view, root, traversal, new LinkedList>(), pass, selector ); + } + } + + private static void dfsIterate( final GraphView view, final ProjectVersionRef node, final ProjectNetTraversal traversal, + final LinkedList> path, final int pass, final GraphEdgeSelector selector ) + { + final List> edges = selector.getSortedOutEdges( view, node ); + if ( edges != null ) + { + for ( final ProjectRelationship edge : edges ) + { + if ( traversal.traverseEdge( edge, path, pass ) ) + { + + if ( !( edge instanceof ParentRelationship ) || !( (ParentRelationship) edge ).isTerminus() ) + { + path.addLast( edge ); + + final ProjectVersionRef target = edge.getTarget() + .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 ) + { + dfsIterate( view, target, traversal, path, pass, selector ); + } + + path.removeLast(); + } + + traversal.edgeTraversed( edge, path, pass ); + if ( traversal.isStopped() ) + { + return; + } + } + + } + } + } + + public static void bfsTraverse( final GraphView view, final ProjectNetTraversal traversal, final int pass, final GraphEdgeSelector selector, + final ProjectVersionRef... roots ) + { + final List>> starts = new ArrayList<>(); + for ( final ProjectVersionRef root : roots ) + { + final List> path = new ArrayList>(); + path.add( new SelfEdge( root ) ); + starts.add( path ); + } + + bfsIterate( view, starts, traversal, pass, selector ); + } + + private static void bfsIterate( final GraphView view, final List>> thisLayer, final ProjectNetTraversal traversal, + final int pass, final GraphEdgeSelector selector ) + { + final List>> nextLayer = new ArrayList>>(); + + for ( final List> path : thisLayer ) + { + if ( path.isEmpty() ) + { + continue; + } + + final ProjectVersionRef node = path.get( path.size() - 1 ) + .getTarget() + .asProjectVersionRef(); + + if ( !path.isEmpty() && ( path.get( 0 ) instanceof SelfEdge ) ) + { + path.remove( 0 ); + } + + final List> edges = selector.getSortedOutEdges( view, 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 ( traversal.isStopped() ) + { + return; + } + } + } + } + } + + if ( !nextLayer.isEmpty() ) + { + Collections.sort( nextLayer, new RelationshipPathComparator() ); + bfsIterate( view, nextLayer, traversal, pass, selector ); + } + } + +} \ No newline at end of file