/* * This file or a portion of this file is licensed under the terms of * the Globus Toolkit Public License, found in file GTPL, or at * http://www.globus.org/toolkit/download/license.html. This notice must * appear in redistributions of this file, with or without modification. * * Redistributions of this Software, with or without modification, must * reproduce the GTPL in: (1) the Software, or (2) the Documentation or * some other similar material which is provided with the Software (if * any). * * Copyright 1999-2004 University of Chicago and The University of * Southern California. All rights reserved. */ package org.griphyn.vdl.planner; import java.io.*; import java.util.*; /** * This class is used to represent the graph form of a workflow. The * graph is represented using adjaceny lists. Each node is the name * of a job in the workflow. The arcs represent dependencies of the * jobs on one another. * * @author Jens-S. Vöckler * @author Yong Zhao * @version $Revision $ */ public class Graph implements Cloneable { /** * The adjacency list representing a graph. */ private Map m_adj; /** * Initializes internal objects to hold the graph. */ private void initialize() { m_adj = new HashMap(); } /** * Default constructor, call the initialize function */ public Graph() { initialize(); } /** * Clones the graph using a deep copy. */ public Object clone() { Graph result = new Graph(); for ( Iterator i = getVertices(); i.hasNext(); ) { String node = (String) i.next(); result.m_adj.put( new String(node), new ArrayList(neighbors(node)) ); } return result; } /** * Reads a stream representing the graph. The format of the stream is: * * <pre> * # of vertices * vertex adjcency_list_of_the_vertex * </pre> * * @param reader is an input file open for reading. */ public Graph( Reader reader ) { String line = null; StringTokenizer token = null; try { LineNumberReader lnr = new LineNumberReader(reader); line = lnr.readLine(); token = new StringTokenizer(line); if ( token.countTokens() != 1 ) throw new Error( "Bad format: number of vertices is first line" ); // number of nodes int n = Integer.parseInt( token.nextToken() ); initialize(); // read rest of graph for ( int u = 0; u < n; ++u ) { line = lnr.readLine(); token = new StringTokenizer(line); if ( token.countTokens() < 1 ) throw new Error( "line " + lnr.getLineNumber() + ": Please specify the vertex and neighbor list." ); // add node to graph String node = token.nextToken(); addVertex(node); // add arcs to graph while ( token.hasMoreTokens() ) { String w = token.nextToken(); ((List) m_adj.get(node)).add(new String(w)); } } } catch (IOException x) { throw new Error("Bad input stream"); } } /** * Provides an iterator over the vertices. * * @return an initialized iterator to walk all vertices. */ public Iterator getVertices() { return m_adj.keySet().iterator(); } /** * Adds a vertex to the adjacency list. The vortex's adjacency * list is initialized to an empty list. If the vortex already * exists, nothing will be done. * * @param node is the name of the vortex to add. * @see #removeVertex( String ) */ public void addVertex( String node ) { if ( ! m_adj.containsKey(node) ) { m_adj.put( new String(node), new ArrayList() ); } } /** * Removes a vortex from the graph. This is an expensive function, * because the vortex must also be removed from all adjacency lists. * * @param node is the name of the vortex to remove. * @see #addVertex( String ) */ public void removeVertex( String node ) { // remove entry for vortex from adjacency list m_adj.remove(node); // remove vortex from all other adjaceny lists for ( Iterator i = getVertices(); i.hasNext(); ) { List v = (List) m_adj.get( (String) i.next() ); v.remove(node); } } /** * Adds a directed edge between two nodes. * * @param v is the source node * @param w is the destination node * @see #removeArc( String, String ) */ public void addArc( String v, String w ) { // skip, if the arc already exists if ( isArc(v,w) ) return; // add arc ((List) m_adj.get(v)).add( new String(w) ); } /** * Removes a directed edge between two nodes. * * @param v is the source node * @param w is the destination node * @see #addArc( String, String ) */ public void removeArc(String v, String w) { ((List) m_adj.get(v)).remove(w); } /** * Predicate to check the existence of a directed edge * between from v to w. * * @param v is the source node * @param w is the destination node */ public boolean isArc( String v, String w ) { return ((List)m_adj.get(v)).contains(w); } /** * Counts the number of incoming arcs (edges) for a given node. * * @param v is the vortex name to count incoming edge for. * @return the number of incoming edges. * @see #outDegree( String ) */ public int inDegree( String v ) { int result = 0; // for all nodes, see if they have an edge to v for ( Iterator i = getVertices(); i.hasNext(); ) { String w = (String) i.next(); if ( isArc(w,v) ) result++; } return result; } /** * Counts the number of outgoing arcs (edges) for a given node. * * @param v is the vortex name to count outgoing edge for. * @return the number of outgoing edges. * @see #inDegree( String ) */ public int outDegree( String v ) { return ((List) m_adj.get(v)).size(); } /** * Determines the neighbors of a given node. This is effectively * a copy of the node's adjacency list. * * @param v is the node to determine the neighbors for * @return a copy of the node's adjacency list. */ public List neighbors(String v) { return new ArrayList( (List) m_adj.get(v) ); } /** * Counts the number of nodes (vertices) in a graph. * @return the number of vertices. */ public int order() { return m_adj.size(); } /** * Counts the number of directed edges (arcs) in the graph. * Undirected edges are counted as two directed edges. * * @return number of directed edges. */ public int size() { int result = 0; for ( Iterator i = m_adj.values().iterator(); i.hasNext(); ) result += ((List) i.next()).size(); return result; } /** * Constructs the reverse graph by inverting the direction of * every arc. * @return a new graph which is the reverse of the current one. */ public Graph reverseGraph() { String v = null; Graph result = new Graph(); // copy all nodes for ( Iterator i = getVertices(); i.hasNext(); ) { v = (String) i.next(); result.addVertex(v); } // copy all edges for ( Iterator i = getVertices(); i.hasNext(); ) { v = (String) i.next(); for ( Iterator j = ((List) m_adj.get(v)).iterator(); j.hasNext(); ) { result.addArc( (String) j.next(), v ); } } return result; } /** * Generates a simple string representation of the graph. The format * of the representation is the same as it is read from a stream. * * @return a complete graph as a single string * @see #Graph( Reader ) */ public String toString() { String newline = System.getProperty("line.separator", "\r\n"); StringBuffer result = new StringBuffer(256); // write order of graph (number of nodes) result.append( order() ).append(newline); // write nodes for ( Iterator i = getVertices(); i.hasNext(); ) { String v = (String) i.next(); result.append(v); // write adjacency list for node v for ( Iterator j = ((List) m_adj.get(v)).iterator(); j.hasNext(); ) { result.append(' ').append( (String) j.next() ); } result.append(newline); } // done return result.toString(); } }