/**
* Copyright 2007-2008 University Of Southern California
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.isi.pegasus.planner.partitioner;
import edu.isi.pegasus.planner.partitioner.graph.GraphNode;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
/**
* Does a topological sort on the Partition.
*
* @author Karan Vahi
* @version $Revision$
*/
public class Topological //implements Iterator
{
/**
* The partition that has to be sorted.
*/
private Partition mPartition;
/**
* An array that contains the number of incoming edges to a node.
*/
private int[] mInDegree;
/**
* A Map that returns the index into mInDegree map for a particular node
* in graph. Maps a ID of the node to an int value, which is the index to
* to the array containing the in degree for each node.
*
* @see #mInDegree
*/
private Map mIndexMap;
/**
* The overloaded constructor.
*
* @param p the partition that has to be sorted.
*/
public Topological( Partition p ){
mPartition = p;
initialize();
}
/**
* Initializes the inDegree for each node of the partition.
*
*/
public void initialize(){
//build up a inDegree map for each node.
int order = mPartition.size();
mInDegree = new int[ order ];
mIndexMap = new HashMap( order );
int index = 0;
//each of the root nodes have in degree of 0
for ( Iterator it = mPartition.getRootNodes().iterator(); it.hasNext(); ){
GraphNode root = (GraphNode)it.next();
mIndexMap.put( root.getID(), new Integer( index ) );
mInDegree[ index++ ] = 0;
}
//determine inDegree for other nodes
for( Iterator it = mPartition.getRelations().entrySet().iterator(); it.hasNext(); ){
Map.Entry entry = (Map.Entry) it.next();
mIndexMap.put( entry.getKey(), new Integer( index) );
mInDegree[ index++ ] = ((List) entry.getValue()).size();
}
//sanity check
if( index != order){
throw new RuntimeException( "Index does not match order of partition " + mPartition.getID());
}
}
/**
* Topologically sorts the partition and returns a List of
* <code>GraphNode</code> elements. The iterator of the list, returns
* the elements in the topological order.
*
* @return List of <code>GraphNode</code> objects
*/
public List sort(){
List l = new LinkedList( );
int order = mPartition.size();
//get all the adjaceny list representation
Map relations = this.childrenRepresentation();
List queue = new LinkedList();
//add all the root nodes to queue first
for( Iterator it = this.mPartition.getRootNodes().iterator(); it.hasNext(); ){
queue.add( ((GraphNode)it.next()).getID() );
}
int index;
while( !queue.isEmpty() ){
String nodeID = (String)queue.remove(0);
l.add( nodeID );
//traverse all the children of the node
if( relations.containsKey( nodeID) ){
for( Iterator it = ( (List)relations.get( nodeID )).iterator(); it.hasNext();){
String childID = (String)it.next();
//remove the edge from node to child by decrementing inDegree
index = index(childID);
mInDegree[ index ] -= 1;
if( mInDegree[ index ] == 0 ){
//add the node to the queue
queue.add( childID );
}
}
}
}
//sanity check
if( l.size() != order ){
throw new RuntimeException( " Partition " + mPartition.getID() + " has a cycle");
}
return l;
}
/**
* Returns a map that is index by GraphNode ID's and each value is the list
* of ID's of children of that GraphNode.
*
* @return Map that contains adjacency list's for each node.
*/
protected Map childrenRepresentation(){
//adjacency list where List contains parents
Map m = new HashMap( mPartition.size() );
for( Iterator it = mPartition.getRelations().entrySet().iterator(); it.hasNext();){
Map.Entry entry = (Map.Entry)it.next();
Object node = entry.getKey();
List parents = (List)entry.getValue();
List children = null;
for( Iterator pit = parents.iterator(); pit.hasNext(); ){
Object parent = pit.next();
//the node should be in parents adjacency list
if( m.containsKey( parent )){
children = (List)m.get(parent);
children.add( node );
}
else{
children = new ArrayList(5);
children.add( node );
m.put( parent, children);
}
}
}
return m;
}
/**
* Returns the index of a particular node. The index is used as an index into
* arrays.
*
* @param id the id of the node.
*
* @return the index
*/
private int index( String id ){
return ((Integer)mIndexMap.get( id )).intValue();
}
}