/*
* 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.dax;
import org.griphyn.vdl.dax.*;
import java.util.*;
import java.io.Writer;
import java.io.IOException;
/**
* This class captures the parent-child relationship between any two
* nodes in a directed acyclic graph. For ease of external
* transportation, the graph is flattened into this two-level form.
* Please note that this presentation is slightly less powerful than the
* true DAGMan form, because for each child, there can be multiple
* parents, but multiple children cannot be grouped.
*
* @author Jens-S. Vöckler
* @author Yong Zhao
* @version $Revision$
*/
public class Child extends DAX implements Cloneable
{
/**
* Captures the list of parent nodes for this child node.
*/
private HashSet m_parentSet;
/**
* Captures the element for which we are constructing dependencies.
*/
private String m_thisChild = null;
/**
* Creates and returns a copy of this object.
* @return a new instance.
*/
public Object clone()
{
Child result = new Child( this.m_thisChild );
for ( Iterator i=this.m_parentSet.iterator(); i.hasNext(); ) {
result.addParent( (String) i.next() );
}
return result;
}
/**
* Default ctor: Constructs a child node w/o any parents
*/
public Child()
{
this.m_parentSet = new HashSet();
}
/**
* Ctor: Constructs a child node.
*
* @param child is the job ID of the child node.
*/
public Child( String child )
{
this.m_thisChild = child;
this.m_parentSet = new HashSet();
}
/**
* Ctor: Constructs a child node.
*
* @param child is the job reference of the child node.
*/
public Child( Job child )
{
this.m_thisChild = child.getID();
this.m_parentSet = new HashSet();
}
/**
* Convenience ctor: Constructs a child node with one parent.
*/
public Child( String child, String parent )
{
this.m_thisChild = child;
this.m_parentSet = new HashSet();
this.m_parentSet.add( parent );
}
/**
* Convenience ctor: Constructs a child node with one parent.
*/
public Child( Job child, Job parent )
{
this.m_thisChild = child.getID();
this.m_parentSet = new HashSet();
this.m_parentSet.add( parent.getID() );
}
/**
* Accessor: Adds a parent job id as dependency to the list of parents.
*
* @param parent is the parent id to add, <b>not</b> the parent reference.
* @see Job
*/
public void addParent( String parent )
{
this.m_parentSet.add( parent );
}
/**
* Accessor: Adds a parent job id as dependency to the list of parents.
*
* @param parent is the parent reference to add
* @see Job
*/
public void addParent( Job parent )
{
this.m_parentSet.add( parent.getID() );
}
/**
* Accessor: Provides an iterator for the parent list.
*
* @return the iterator for all dependencies.
* @see Job
*/
public Iterator iterateParent()
{
return this.m_parentSet.iterator();
}
/**
* Accessor: Obtains the child identifier.
*
* @return the name of the current child, or <code>null</code>, if
* the element is hollow.
* @see #setChild( String )
*/
public String getChild()
{
return this.m_thisChild;
}
/**
* Accessor: Obtains a parent, iff it is in the bag.
*
* @param name is the parent id to look up.
* @return true if the parent is know, false otherwise.
*/
public boolean getParent( String name )
{
return this.m_parentSet.contains(name);
}
// /**
// * Accessor: Obtains the complete parental dependencies (one level).
// *
// * @return an array with all parent IDs inside.
// * @see Job
// */
// public String[] getParent()
// {
// int size = this.m_parentSet.size();
// String[] mArray = new String[size];
// for (Iterator i=this.m_parentSet.iterator(); i.hasNext(); ) {
// mArray[index] = (String) i.next();
// }
// return mArray;
// }
/**
* Accessor: Obtains the count of parental dependencies.
*
* @return the number of parents.
* @see Job
*/
public int getParentCount()
{
return this.m_parentSet.size();
}
/**
* Accessor: Removes all parental dependencies.
* @see Job
*/
public void removeAllParent()
{
this.m_parentSet.clear();
}
/**
* Accessor: Removes a parent name from the bag.
*
* @param name is the name of the parent ID to remove.
* @return true, if the parent was removed, false, if it was not present.
* @see Job
* @see java.util.HashSet#remove(Object)
*/
public boolean removeParent( String name )
{
return this.m_parentSet.remove(name);
}
/**
* Accessor: Sets the identifier for this dependency child.
*
* @param id is the job identifier.
* @see #getChild()
*/
public void setChild( String id )
{
this.m_thisChild = id;
}
/**
* Accessor: Sets the identifier for this dependency child.
*
* @param job is a job reference.
* @see #getChild()
*/
public void setChild( Job job )
{
this.m_thisChild = job.getID();
}
/**
* Updates the identifiers for child and parents from a mapping.
* @param mapping is the mapping between old and new identifier
* @return a new instance with mapped identifiers. If none of the
* old identifiers in mapping are in the child, the result is the
* same as a {@link #clone()}.
*/
public Child updateChild( java.util.Map mapping )
{
Child result = null;
if ( mapping.containsKey( this.m_thisChild ) ) {
// child name itself needs mapping
result = new Child( (String) mapping.get(this.m_thisChild) );
} else {
// child name can be copied
result = new Child( this.m_thisChild );
}
for ( Iterator i=this.m_parentSet.iterator(); i.hasNext(); ) {
String parent = (String) i.next();
if ( mapping.containsKey( parent ) ) {
result.addParent( (String) mapping.get(parent) );
} else {
result.addParent( parent );
}
}
return result;
}
/**
* Converts the active state into something meant for human consumption.
* The method will be called when recursively traversing the instance
* tree.
*
* @param stream is a stream opened and ready for writing. This can also
* be a string stream for efficient output.
*/
public void toString( Writer stream )
throws IOException
{
// only do anything for children with parents!
if ( this.m_parentSet.size() > 0 ) {
stream.write( " " );
stream.write( "CHILD " );
stream.write( escape(this.m_thisChild) );
stream.write( " PARENT" );
for ( Iterator i=this.m_parentSet.iterator(); i.hasNext(); ) {
stream.write( ' ' );
stream.write( escape((String) i.next()) );
}
stream.write( System.getProperty("line.separator","\r\n") );
}
}
/**
* Dump the state of the current element as XML output. This function
* traverses all sibling classes as necessary, and converts the data
* into pretty-printed XML output. The stream interface should be able
* to handle large output efficiently.
*
* @param stream is a stream opened and ready for writing. This can also
* be a string stream for efficient output.
* @param indent is a <code>String</code> of spaces used for pretty
* printing. The initial amount of spaces should be an empty string.
* The parameter is used internally for the recursive traversal.
* @param namespace is the XML schema namespace prefix. If neither
* empty nor null, each element will be prefixed with this prefix,
* and the root element will map the XML namespace.
* @exception IOException if something fishy happens to the stream.
*/
public void toXML( Writer stream, String indent, String namespace )
throws IOException
{
// only do anything for children with parents!
if ( this.m_parentSet.size() > 0 ) {
String newline = System.getProperty( "line.separator", "\r\n" );
String tag = ( namespace != null && namespace.length() > 0 ) ?
namespace + ":child" : "child";
String tag2 = ( namespace != null && namespace.length() > 0 ) ?
namespace + ":parent" : "parent";
// open tag
if ( indent != null && indent.length() > 0 ) stream.write( indent );
stream.write( '<' );
stream.write( tag );
writeAttribute( stream, " ref=\"", this.m_thisChild );
stream.write( '>' );
if ( indent != null ) stream.write( newline );
String newindent = indent==null ? null : indent+" ";
for ( Iterator i=this.m_parentSet.iterator(); i.hasNext(); ) {
if ( indent != null && indent.length() > 0 ) stream.write( newindent );
stream.write( '<' );
stream.write( tag2 );
writeAttribute( stream, " ref=\"", (String) i.next() );
stream.write( "/>" );
if ( indent != null ) stream.write( newline );
}
if ( indent != null && indent.length() > 0 ) stream.write( indent );
stream.write( "</" );
stream.write( tag );
stream.write( '>' );
if ( indent != null ) stream.write( newline );
}
}
}