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 extends ProjectRelationship>> getRelationshipsDeclaredBy( final GraphView view, final ProjectVersionRef root )
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Collection extends ProjectRelationship>> 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
new file mode 100644
index 00000000..1c704df3
--- /dev/null
+++ b/drivers/jung/src/main/java/org/commonjava/maven/atlas/graph/spi/jung/CycleDetectionTraversal.java
@@ -0,0 +1,111 @@
+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;
+
+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<>();
+
+ 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;
+ }
+
+ public Set getCycles()
+ {
+ return cycles;
+ }
+
+ @Override
+ public boolean preCheck( final ProjectRelationship> relationship, final List> path, final int pass )
+ {
+ boolean found = false;
+ if ( existingCycleParticipants.contains( relationship ) || hits.contains( relationship ) )
+ {
+ logger.info( "%s is part of an existing cycle. Skip traversal.", relationship );
+ found = true;
+ }
+
+ if ( !found && 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;
+ }
+
+ if ( !found )
+ {
+ 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 ( 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 86d6c1d0..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;
@@ -28,30 +29,29 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
+import java.util.Iterator;
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.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;
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.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.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.SingleVersion;
@@ -63,61 +63,119 @@
public class JungEGraphDriver
implements GraphDatabaseDriver
{
- // private final Logger logger = new Logger( getClass() );
+ private static final String PATHS = "paths";
- private DirectedGraph> graph = new DirectedSparseMultigraph<>();
+ 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 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 extends ProjectRelationship>> 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 extends ProjectRelationship>> 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, selector, 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,85 +250,64 @@ 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 )
{
- if ( !graph.containsVertex( 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 ) )
- {
- // logger.info( "Adding incomplete target: %s", target );
- incompleteSubgraphs.add( target );
- }
- if ( !graph.containsVertex( target ) )
- {
- // logger.info( "Adding node: %s", target );
- graph.addVertex( target.asProjectVersionRef() );
- addGA( target );
- }
+ targets.add( target );
- final List> 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 );
}
- for ( final ProjectRelationship> rel : rels )
- {
- if ( skipped.contains( rel ) )
- {
- continue;
- }
+ final CycleDetectionTraversal traversal = new CycleDetectionTraversal( cycles, skipped, rels );
+ dfsTraverse( GraphView.GLOBAL, traversal, 0, selector, 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,344 +324,292 @@ 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 )
{
- dfsTraverse( view, traversal, 0, root );
+ final PathState state = pathStates.get( ref );
+ if ( state != null )
+ {
+ result.addAll( state.getVisiblePaths() );
+ }
}
- return traversal.getPaths();
- }
-
- @Override
- public boolean introducesCycle( final GraphView view, final ProjectRelationship> rel )
- {
- final CycleDetectionTraversal traversal = new CycleDetectionTraversal( rel );
-
- dfsTraverse( view, traversal, 0, rel.getTarget()
- .asProjectVersionRef() );
-
- return !traversal.getCycles()
- .isEmpty();
+ return result;
}
- @Override
- public Set getAllProjects( final GraphView view )
+ private void findPathsToProjects( final Set roots, final ProjectRelationshipFilter filter, final GraphView view,
+ final boolean detectOnly, final Map pathStates,
+ final Collection refs )
{
- return new HashSet( graph.getVertices() );
- }
+ boolean reTraverse = false;
+ for ( final ProjectVersionRef ref : refs )
+ {
+ if ( !pathStates.containsKey( ref ) )
+ {
+ reTraverse = true;
+ break;
+ }
+ }
- @Override
- public void traverse( final GraphView view, final ProjectNetTraversal traversal, final EProjectNet net, final ProjectVersionRef root )
- throws GraphDriverException
- {
- final int passes = traversal.getRequiredPasses();
- for ( int i = 0; i < passes; i++ )
+ if ( reTraverse )
{
- traversal.startTraverse( i, net );
+ logger.info( "Re-traversing to capture path states for: %s", refs );
+ final PathMappingTraversal traversal = new PathMappingTraversal( filter, pathStates );
- switch ( traversal.getType( i ) )
+ if ( roots == null || roots.isEmpty() )
{
- case breadth_first:
- {
- bfsTraverse( view, traversal, i, root );
- break;
- }
- case depth_first:
- {
- dfsTraverse( view, traversal, i, root );
- break;
- }
+ logger.warn( "Cannot retrieve paths targeting %s. No roots specified for this project network!", join( refs, ", " ) );
+ return;
}
- traversal.endTraverse( i, net );
+ dfsTraverse( view, traversal, 0, selector, roots.toArray( new ProjectVersionRef[roots.size()] ) );
}
}
- // TODO: Implement without recursion.
- private void dfsTraverse( final GraphView view, final ProjectNetTraversal traversal, final int pass, final ProjectVersionRef root )
+ private void findPathsToProjects( final Set roots, final ProjectRelationshipFilter filter, final GraphView view,
+ final boolean detectOnly, final Map pathStates, final ProjectVersionRef... refs )
{
- 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 )
+ boolean reTraverse = false;
+ for ( final ProjectVersionRef ref : refs )
{
- for ( final ProjectRelationship> edge : edges )
+ if ( !pathStates.containsKey( ref ) )
{
- path.addLast( edge );
- if ( traversal.traverseEdge( edge, path, pass ) )
- {
-
- if ( !( edge instanceof ParentRelationship ) || !( (ParentRelationship) edge ).isTerminus() )
- {
- 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 );
- }
-
- }
-
- traversal.edgeTraversed( edge, path, pass );
- }
-
- path.removeLast();
+ reTraverse = true;
+ break;
}
}
- }
- // TODO: Implement without recursion.
- private void bfsTraverse( final GraphView view, final ProjectNetTraversal traversal, final int pass, final ProjectVersionRef root )
- {
- final List> path = new ArrayList>();
- path.add( new SelfEdge( root ) );
+ 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;
+ }
- bfsIterate( view, Collections.singletonList( path ), traversal, pass );
+ dfsTraverse( view, traversal, 0, selector, roots.toArray( new ProjectVersionRef[roots.size()] ) );
+ }
}
- private void bfsIterate( final GraphView view, final List>> thisLayer, final ProjectNetTraversal traversal,
- final int pass )
+ private void findPathsToRelationships( final Set roots, final ProjectRelationshipFilter filter, final GraphView view,
+ final boolean detectOnly, final Map pathStates,
+ final ProjectRelationship>... rels )
{
- final List>> nextLayer = new ArrayList>>();
-
- for ( final List> path : thisLayer )
+ boolean reTraverse = false;
+ for ( final ProjectRelationship> rel : rels )
{
- if ( path.isEmpty() )
+ if ( !pathStates.containsKey( rel.getTarget()
+ .asProjectVersionRef() ) )
{
- continue;
+ reTraverse = true;
+ break;
}
+ }
- final ProjectVersionRef node = path.get( path.size() - 1 )
- .getTarget()
- .asProjectVersionRef();
-
- if ( !path.isEmpty() && ( path.get( 0 ) instanceof SelfEdge ) )
- {
- path.remove( 0 );
- }
+ if ( reTraverse )
+ {
+ final FilteringTraversal traversal = new FilteringTraversal( filter, true );
- final List> edges = getSortedOutEdges( view, node );
- if ( edges != null )
+ if ( roots == null || roots.isEmpty() )
{
- for ( final ProjectRelationship> 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 ) )
- {
- // Don't account for terminal parent relationship.
- if ( !( edge instanceof ParentRelationship ) || !( (ParentRelationship) edge ).isTerminus() )
- {
- nextLayer.add( nextPath );
- }
-
- traversal.edgeTraversed( edge, path, pass );
- }
- }
+ logger.warn( "Cannot retrieve paths targeting %s. No roots specified for this project network!", join( rels, ", " ) );
+ return;
}
- }
- if ( !nextLayer.isEmpty() )
- {
- Collections.sort( nextLayer, new RelationshipPathComparator() );
- bfsIterate( view, nextLayer, traversal, pass );
+ dfsTraverse( view, traversal, 0, selector, roots.toArray( new ProjectVersionRef[roots.size()] ) );
}
}
- private List> getSortedOutEdges( final GraphView view, final ProjectVersionRef node )
+ @Override
+ public boolean introducesCycle( final GraphView view, final ProjectRelationship> rel )
{
- Collection> unsorted = graph.getOutEdges( node.asProjectVersionRef() );
- if ( unsorted == null )
- {
- return null;
- }
-
- unsorted = new ArrayList>( unsorted );
+ final SingleCycleDetectionTraversal traversal = new SingleCycleDetectionTraversal( rel );
- RelationshipUtils.filterTerminalParents( unsorted );
+ dfsTraverse( view, traversal, 0, selector, rel.getTarget()
+ .asProjectVersionRef() );
- final List> sorted = new ArrayList>( imposeSelections( view, unsorted ) );
- Collections.sort( sorted, new RelationshipComparator() );
-
- return sorted;
+ return !traversal.getCycles()
+ .isEmpty();
}
- private static final class SelfEdge
- extends AbstractProjectRelationship
+ @Override
+ public Set getAllProjects( final GraphView view )
{
+ return findVisibleProjects( getRoots( view, false ), view.getFilter(), view, graph.getVertices() );
+ }
- private static final long serialVersionUID = 1L;
-
- SelfEdge( final ProjectVersionRef ref )
- {
- super( (URI) null, null, ref.asProjectVersionRef(), ref.asProjectVersionRef(), 0 );
- }
+ @Override
+ public boolean containsProject( final GraphView view, final ProjectVersionRef ref )
+ {
+ final Set singleton = new HashSet<>();
+ singleton.add( ref );
- @Override
- public ArtifactRef getTargetArtifact()
- {
- return getTarget().asPomArtifact();
- }
+ final Set visibleProjects = findVisibleProjects( getRoots( view, false ), view.getFilter(), view, singleton );
+ return !visibleProjects.isEmpty() && !incompleteSubgraphs.contains( ref.asProjectVersionRef() );
+ }
- @Override
- public ProjectRelationship selectDeclaring( final SingleVersion version )
- {
- return selectDeclaring( version, false );
- }
+ @Override
+ public boolean containsRelationship( final GraphView view, final ProjectRelationship> rel )
+ {
+ final Map pathStates = getPathStates( view );
- @Override
- public ProjectRelationship selectDeclaring( final SingleVersion version, final boolean force )
- {
- return new SelfEdge( getDeclaring().selectVersion( version, force ) );
- }
+ findPathsToRelationships( getRoots( view, false ), view.getFilter(), view, true, pathStates, rel );
- @Override
- public ProjectRelationship selectTarget( final SingleVersion version )
- {
- return selectTarget( version, false );
- }
+ final PathState state = pathStates.get( rel.getTarget()
+ .asProjectVersionRef() );
- @Override
- public ProjectRelationship selectTarget( final SingleVersion version, final boolean force )
- {
- return new SelfEdge( getDeclaring().selectVersion( version, force ) );
- }
+ return state != null && state.containsVisibleRelationship( rel );
+ }
+ @Override
+ public void close()
+ throws IOException
+ {
+ // NOP; stored in memory.
}
- // @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;
- // }
+ // FIXME: This is incredibly expensive!
+ @Override
+ public boolean isMissing( final GraphView view, final ProjectVersionRef project )
+ {
+ return getMissingProjects( view ).contains( project.asProjectVersionRef() );
+ }
+ // FIXME: This is incredibly expensive!
@Override
- public boolean containsProject( final GraphView view, final ProjectVersionRef ref )
+ public boolean hasMissingProjects( final GraphView view )
{
- return graph.containsVertex( ref.asProjectVersionRef() ) && !incompleteSubgraphs.contains( ref.asProjectVersionRef() );
+ return !getMissingProjects( view ).isEmpty();
}
+ // FIXME: This is incredibly expensive!
@Override
- public boolean containsRelationship( final GraphView view, final ProjectRelationship> rel )
+ public Set getMissingProjects( final GraphView view )
{
- return graph.containsEdge( rel );
+ return findVisibleProjects( getRoots( view, true ), view.getFilter(), view, incompleteSubgraphs );
}
- public void restrictProjectMembership( final Collection refs )
+ private Set getRoots( final GraphView view, final boolean incomplete )
{
- final Set> rels = new HashSet>();
- for ( final ProjectVersionRef ref : refs )
+ Set roots = view.getRoots();
+ if ( roots == null || roots.isEmpty() )
{
- final Collection> edges = graph.getOutEdges( ref.asProjectVersionRef() );
- if ( edges != null )
+ roots = new HashSet<>( graph.getVertices() );
+ if ( !incomplete )
{
- rels.addAll( edges );
+ roots.removeAll( incompleteSubgraphs );
}
}
- restrictRelationshipMembership( rels );
+ return roots;
}
- public void restrictRelationshipMembership( final Collection> rels )
+ // FIXME: tighter synchronization
+ private synchronized Set findVisibleProjects( final Set roots, final ProjectRelationshipFilter filter,
+ final GraphView view, final Collection originalRefs )
{
- graph = new DirectedSparseMultigraph>();
- incompleteSubgraphs.clear();
- variableSubgraphs.clear();
+ 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 );
+ }
+ }
+ }
- addRelationships( rels.toArray( new ProjectRelationship>[] {} ) );
+ for ( final ProjectVersionRef ref : refs )
+ {
+ if ( roots.contains( ref ) )
+ {
+ found.add( ref );
+ }
+ }
- recomputeIncompleteSubgraphs();
+ // logger.info( "Got %d missing projects: %s", result.size(), result );
+ return found;
}
- @Override
- public void close()
- throws IOException
+ private Map getPathStates( final GraphView view )
{
- // NOP; stored in memory.
+ return getPathStates( view, true );
}
- // @Override
- // public boolean isDerivedFrom( final EGraphDriver driver )
- // {
- // return false;
- // }
-
- @Override
- public boolean isMissing( final GraphView view, final ProjectVersionRef project )
+ private Map getPathStates( final GraphView view, final boolean create )
{
- return !graph.containsVertex( project.asProjectVersionRef() );
+ @SuppressWarnings( "unchecked" )
+ Map pathStates = (Map) getViewCache( view, PATHS );
+ if ( pathStates == null && create )
+ {
+ pathStates = new HashMap<>();
+ setViewCache( view, PATHS, pathStates );
+ }
+
+ return pathStates;
}
- @Override
- public boolean hasMissingProjects( final GraphView view )
+ private void setViewCache( final GraphView view, final String key, final Object value )
{
- return !incompleteSubgraphs.isEmpty();
+ Map viewCaches = caches.get( view );
+ if ( viewCaches == null )
+ {
+ viewCaches = new HashMap<>();
+ caches.put( view, viewCaches );
+ }
+
+ viewCaches.put( key, value );
}
- @Override
- public Set getMissingProjects( final GraphView view )
+ private Object getViewCache( final GraphView view, final String key )
{
- final Set result = new HashSet( incompleteSubgraphs );
- // logger.info( "Got %d missing projects: %s", result.size(), result );
- return result;
+ 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 +684,6 @@ public boolean isCycleParticipant( final GraphView view, final ProjectVersionRef
return false;
}
- @Override
- public void recomputeIncompleteSubgraphs()
- {
- for ( final ProjectVersionRef vertex : getAllProjects( GraphView.GLOBAL ) )
- {
- final Collection extends ProjectRelationship>> outEdges = getRelationshipsDeclaredBy( GraphView.GLOBAL, vertex );
- if ( outEdges != null && !outEdges.isEmpty() )
- {
- incompleteSubgraphs.remove( vertex );
- }
- }
- }
-
@Override
public Map getMetadata( final ProjectVersionRef ref )
{
@@ -806,171 +778,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 )
{
@@ -1094,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/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 extends List>> getVisiblePaths()
+ {
+ return visibleFiltersByPath.keySet();
+ }
+
+ public Collection extends ProjectRelationship>> 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/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/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/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/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