/*
* Copyright 2008 Network Engine for Objects in Lund AB [neotechnology.com]
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.graphalgo.impl.shortestpath;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
/**
* This is a holder for some utility functions regarding paths, such as
* constructing them from sets of predecessors or counting them. These functions
* are lifted out here because they can be used by algorithms for too different
* problems.
* @author Patrik Larsson
*/
public class Util
{
/**
* Constructs a path to a given node, for a given set of predecessors
* @param node
* The start node
* @param predecessors
* The predecessors set
* @param includeNode
* Boolean which determines if the start node should be included
* in the paths
* @param backwards
* Boolean, if true the order of the nodes in the paths will be
* reversed
* @return A path as a list of nodes.
*/
public static List<Node> constructSinglePathToNodeAsNodes( Node node,
Map<Node,List<Relationship>> predecessors, boolean includeNode,
boolean backwards )
{
List<PropertyContainer> singlePathToNode = constructSinglePathToNode(
node, predecessors, includeNode, backwards );
Iterator<PropertyContainer> iterator = singlePathToNode.iterator();
// When going backwards and not including the node the first element is
// a relationship. Thus skip it.
if ( backwards && !includeNode && iterator.hasNext() )
{
iterator.next();
}
LinkedList<Node> path = new LinkedList<Node>();
while ( iterator.hasNext() )
{
path.addLast( (Node) iterator.next() );
if ( iterator.hasNext() )
{
iterator.next();
}
}
return path;
}
/**
* Constructs a path to a given node, for a given set of predecessors
* @param node
* The start node
* @param predecessors
* The predecessors set
* @param backwards
* Boolean, if true the order of the nodes in the paths will be
* reversed
* @return A path as a list of relationships.
*/
public static List<Relationship> constructSinglePathToNodeAsRelationships(
Node node, Map<Node,List<Relationship>> predecessors, boolean backwards )
{
List<PropertyContainer> singlePathToNode = constructSinglePathToNode(
node, predecessors, true, backwards );
Iterator<PropertyContainer> iterator = singlePathToNode.iterator();
// Skip the first, it is a node
if ( iterator.hasNext() )
{
iterator.next();
}
LinkedList<Relationship> path = new LinkedList<Relationship>();
while ( iterator.hasNext() )
{
path.addLast( (Relationship) iterator.next() );
if ( iterator.hasNext() )
{
iterator.next();
}
}
return path;
}
/**
* Constructs a path to a given node, for a given set of predecessors. The
* result is a list of alternating Node/Relationship.
* @param node
* The start node
* @param predecessors
* The predecessors set
* @param includeNode
* Boolean which determines if the start node should be included
* in the paths
* @param backwards
* Boolean, if true the order of the nodes in the paths will be
* reversed
* @return A path as a list of alternating Node/Relationship.
*/
public static List<PropertyContainer> constructSinglePathToNode( Node node,
Map<Node,List<Relationship>> predecessors, boolean includeNode,
boolean backwards )
{
LinkedList<PropertyContainer> path = new LinkedList<PropertyContainer>();
if ( includeNode )
{
if ( backwards )
{
path.addLast( node );
}
else
{
path.addFirst( node );
}
}
Node currentNode = node;
List<Relationship> currentPreds = predecessors.get( currentNode );
// Traverse predecessors until we have added a node without predecessors
while ( currentPreds != null && currentPreds.size() != 0 )
{
// Get next node
Relationship currentRelationship = currentPreds.get( 0 );
currentNode = currentRelationship.getOtherNode( currentNode );
// Add current
if ( backwards )
{
path.addLast( currentRelationship );
path.addLast( currentNode );
}
else
{
path.addFirst( currentRelationship );
path.addFirst( currentNode );
}
// Continue with the next node
currentPreds = predecessors.get( currentNode );
}
return path;
}
/**
* Constructs all paths to a given node, for a given set of predecessors
* @param node
* The start node
* @param predecessors
* The predecessors set
* @param includeNode
* Boolean which determines if the start node should be included
* in the paths
* @param backwards
* Boolean, if true the order of the nodes in the paths will be
* reversed
* @return
*/
public static List<List<Node>> constructAllPathsToNodeAsNodes( Node node,
Map<Node,List<Relationship>> predecessors, boolean includeNode,
boolean backwards )
{
return new LinkedList<List<Node>>(
constructAllPathsToNodeAsNodeLinkedLists( node, predecessors,
includeNode, backwards ) );
}
/**
* Same as constructAllPathsToNodeAsNodes, but different return type
*/
protected static List<LinkedList<Node>> constructAllPathsToNodeAsNodeLinkedLists(
Node node, Map<Node,List<Relationship>> predecessors,
boolean includeNode, boolean backwards )
{
List<LinkedList<Node>> paths = new LinkedList<LinkedList<Node>>();
List<Relationship> current = predecessors.get( node );
// First build all paths to this node's predecessors
if ( current != null )
{
for ( Relationship r : current )
{
Node n = r.getOtherNode( node );
paths.addAll( constructAllPathsToNodeAsNodeLinkedLists( n,
predecessors, true, backwards ) );
}
}
// If no paths exists to this node, just create an empty one (which will
// have this node added to it)
if ( paths.isEmpty() )
{
paths.add( new LinkedList<Node>() );
}
// Then add this node to all those paths
if ( includeNode )
{
for ( LinkedList<Node> path : paths )
{
if ( backwards )
{
path.addFirst( node );
}
else
{
path.addLast( node );
}
}
}
return paths;
}
/**
* Constructs all paths to a given node, for a given set of predecessors
* @param node
* The start node
* @param predecessors
* The predecessors set
* @param includeNode
* Boolean which determines if the start node should be included
* in the paths
* @param backwards
* Boolean, if true the order of the nodes in the paths will be
* reversed
* @return List of lists of alternating Node/Relationship.
*/
public static List<List<PropertyContainer>> constructAllPathsToNode(
Node node, Map<Node,List<Relationship>> predecessors,
boolean includeNode, boolean backwards )
{
return new LinkedList<List<PropertyContainer>>(
constructAllPathsToNodeAsLinkedLists( node, predecessors,
includeNode, backwards ) );
}
/**
* Same as constructAllPathsToNode, but different return type
*/
protected static List<LinkedList<PropertyContainer>> constructAllPathsToNodeAsLinkedLists(
Node node, Map<Node,List<Relationship>> predecessors,
boolean includeNode, boolean backwards )
{
List<LinkedList<PropertyContainer>> paths = new LinkedList<LinkedList<PropertyContainer>>();
List<Relationship> current = predecessors.get( node );
// First build all paths to this node's predecessors
if ( current != null )
{
for ( Relationship r : current )
{
Node n = r.getOtherNode( node );
List<LinkedList<PropertyContainer>> newPaths = constructAllPathsToNodeAsLinkedLists(
n, predecessors, true, backwards );
paths.addAll( newPaths );
// Add the relationship
for ( LinkedList<PropertyContainer> path : newPaths )
{
if ( backwards )
{
path.addFirst( r );
}
else
{
path.addLast( r );
}
}
}
}
// If no paths exists to this node, just create an empty one (which will
// have this node added to it)
if ( paths.isEmpty() )
{
paths.add( new LinkedList<PropertyContainer>() );
}
// Then add this node to all those paths
if ( includeNode )
{
for ( LinkedList<PropertyContainer> path : paths )
{
if ( backwards )
{
path.addFirst( node );
}
else
{
path.addLast( node );
}
}
}
return paths;
}
/**
* Constructs all paths to a given node, for a given set of predecessors.
* @param node
* The start node
* @param predecessors
* The predecessors set
* @param backwards
* Boolean, if true the order of the nodes in the paths will be
* reversed
* @return List of lists of relationships.
*/
public static List<List<Relationship>> constructAllPathsToNodeAsRelationships(
Node node, Map<Node,List<Relationship>> predecessors, boolean backwards )
{
return new LinkedList<List<Relationship>>(
constructAllPathsToNodeAsRelationshipLinkedLists( node,
predecessors, backwards ) );
}
/**
* Same as constructAllPathsToNodeAsRelationships, but different return type
*/
protected static List<LinkedList<Relationship>> constructAllPathsToNodeAsRelationshipLinkedLists(
Node node, Map<Node,List<Relationship>> predecessors, boolean backwards )
{
List<LinkedList<Relationship>> paths = new LinkedList<LinkedList<Relationship>>();
List<Relationship> current = predecessors.get( node );
// First build all paths to this node's predecessors
if ( current != null )
{
for ( Relationship r : current )
{
Node n = r.getOtherNode( node );
List<LinkedList<Relationship>> newPaths = constructAllPathsToNodeAsRelationshipLinkedLists(
n, predecessors, backwards );
paths.addAll( newPaths );
// Add the relationship
for ( LinkedList<Relationship> path : newPaths )
{
if ( backwards )
{
path.addFirst( r );
}
else
{
path.addLast( r );
}
}
}
}
// If no paths exists to this node, just create an empty one
if ( paths.isEmpty() )
{
paths.add( new LinkedList<Relationship>() );
}
return paths;
}
/**
* This can be used for counting the number of paths from the start node
* (implicit from the predecessors) and some target nodes.
*/
public static class PathCounter
{
Map<Node,List<Relationship>> predecessors;
Map<Node,Integer> pathCounts = new HashMap<Node,Integer>();
public PathCounter( Map<Node,List<Relationship>> predecessors )
{
super();
this.predecessors = predecessors;
}
public int getNumberOfPathsToNode( Node node )
{
Integer i = pathCounts.get( node );
if ( i != null )
{
return i;
}
List<Relationship> preds = predecessors.get( node );
if ( preds == null || preds.size() == 0 )
{
return 1;
}
int result = 0;
for ( Relationship relationship : preds )
{
result += getNumberOfPathsToNode( relationship
.getOtherNode( node ) );
}
pathCounts.put( node, result );
return result;
}
}
/**
* This can be used to generate the inverse of a structure with
* predecessors, i.e. the successors.
* @param predecessors
* @return
*/
public static Map<Node,List<Relationship>> reversedPredecessors(
Map<Node,List<Relationship>> predecessors )
{
Map<Node,List<Relationship>> result = new HashMap<Node,List<Relationship>>();
Set<Node> keys = predecessors.keySet();
for ( Node node : keys )
{
List<Relationship> preds = predecessors.get( node );
for ( Relationship relationship : preds )
{
Node otherNode = relationship.getOtherNode( node );
// We add node as a predecessor to otherNode, instead of the
// other way around
List<Relationship> otherPreds = result.get( otherNode );
if ( otherPreds == null )
{
otherPreds = new LinkedList<Relationship>();
result.put( otherNode, otherPreds );
}
otherPreds.add( relationship );
}
}
return result;
}
}