/** * 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.graph; import java.util.List; 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 TopologicalSortIterator implements Iterator { /** * The partition that has to be sorted. */ private Graph mGraph; /** * 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 internal list of nodes that contains the nodes to be traversed. */ private List<GraphNode> mQueue ; /** * The number of nodes in the graph. */ private int mOrder; /** * The overloaded constructor. * * @param p the graph that has to be sorted. */ public TopologicalSortIterator( Graph graph ){ mGraph = graph; initialize(); mOrder = mGraph.size(); mQueue = new LinkedList(); //add all the root nodes to queue first for( Iterator it = this.mGraph.getRoots().iterator(); it.hasNext(); ){ mQueue.add( (GraphNode)it.next() ); } } /** * Initializes the inDegree for each node of the partition. * */ public void initialize(){ //build up a inDegree map for each node. int order = mGraph.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 = mGraph.getRoots().iterator(); it.hasNext(); ){ GraphNode root = (GraphNode)it.next(); mIndexMap.put( root.getID(), new Integer( index ) ); mInDegree[ index++ ] = 0; } //determine inDegree for other nodes //in degree for a node is the number of incoming edges/parents of a node for( Iterator<GraphNode> it = mGraph.nodeIterator(); it.hasNext(); ){ GraphNode node = it.next(); if( node.getParents().isEmpty() ){ //node is a root. indegree already assigned continue; } mIndexMap.put( node.getID(), new Integer( index) ); mInDegree[ index++ ] = node.getParents().size(); } //sanity check if( index != order){ throw new RuntimeException( "Index does not match order of partition " ); } } /** * Returns whether there are more nodes to be traversed in the graph or not. * * @return boolean */ public boolean hasNext() { return !mQueue.isEmpty(); } /** * Returns the next node to be traversed * * @return */ public Object next() { GraphNode node = mQueue.remove( 0 ); String nodeID = node.getID(); //traverse all the children of the node // GraphNode n = null; for( Iterator<GraphNode> it = node.getChildren().iterator(); it.hasNext() ;){ GraphNode child = it.next(); String childID = child.getID(); //remove the edge from node to child by decrementing inDegree int index = index(childID); mInDegree[ index ] -= 1; if( mInDegree[ index ] == 0 ){ //add the node to the queue mQueue.add( child ); } } return node; } /** * Removes a node from the graph. Operation not supported as yet. */ public void remove() { throw new UnsupportedOperationException("Not supported yet."); } /** * 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(); } }