/*
* 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.classes;
import edu.isi.pegasus.common.util.Separator;
import java.util.*;
import java.io.IOException;
import java.io.Writer;
import java.io.Serializable;
/**
* <code>Derivation</code> is an implementation of an abstract VDL
* <code>Definition</code>. A derivation describes the mutable part
* concerning input, processing, and output (IPO) when calling an
* application. The environment is part of the capture.
*
* A derivation parametrizes the template provided by a
* <code>Transformation</code> with actual values. Think of a derivation
* as something akin to a C function call. The derivation provides the
* actual parameter to a job. The immutable parts are hidden in a
* <code>Transformation</code>.
*
* @author Jens-S. Vöckler
* @author Yong Zhao
* @version $Revision$
*
* @see Definition
* @see Definitions
* @see Transformation
*/
public class Derivation extends Definition // thus implements VDL
implements HasPass, Serializable
{
/**
* Though most <code>Derivation</code>s may have a name of their own,
* most of the times, though, derivations are anonymous. A derivation
* must provide the name of the <code>Transformation</code> that
* it calls, though.
*
* @see Definition
* @see Transformation
*/
private String m_uses;
/**
* The namespace in which a derivation resides can differ from the
* namespace that the transformation lives in. This argument provides
* the namespace of the <code>Transformation</code> to call.
* @see Definition
* @see Transformation
*/
private String m_usesspace;
/**
* Any <code>Transformation</code> may exist in multiple versions.
* This argument specifies the minimum permissable version that can
* be used. FIXME: versioning is not really supported.
*/
private String m_maxIncludeVersion;
/**
* Any <code>Transformation</code> may exist in multiple versions.
* This argument specifies the maximum permissable version that can
* be used. FIXME: versioning is not really supported.
*/
private String m_minIncludeVersion;
/**
* Actual arguments used when calling a {@link Transformation} are
* matched up with the formal arguments of the transformation by their
* names.
*/
private TreeMap m_passMap;
/**
* Type accessor for quick distinction between definitions.
* @return the value of {@link Definition#DERIVATION}
*/
public int getType()
{
return Definition.DERIVATION;
}
/**
* ctor.
*/
public Derivation() {
super();
this.m_passMap = new TreeMap();
}
/**
* Convenience ctor: Names a derivation and the used
* <code>Transformation</code>
*
* @param name is the name of the <code>Derivation</code>
* @param uses is the name of the <code>Transformation</code>
* @see Transformation
*/
public Derivation( String name, String uses ) {
super(name);
this.m_passMap = new TreeMap();
this.m_uses = uses;
}
/**
* Convenience ctor: Names a derivation and supplies the used
* <code>Transformation</code> as well as the permissable version
* range.
*
* @param name is the name of the <code>Derivation</code>.
* @param uses is the name of the <code>Transformation</code>.
* @param min is the minimum inclusive permissable version.
* @param max is the maximum inclusive permissable version.
* @see Transformation
*/
public Derivation( String name, String uses, String min, String max ) {
super(name);
this.m_passMap = new TreeMap();
this.m_uses = uses;
this.m_minIncludeVersion = min;
this.m_maxIncludeVersion = max;
}
/**
* Complete ctor: Constructs the full three part <code>Derivation</code>
* identifier, and four part <code>Transformation</code> mapper.
*
* @param ns is then namespace of the <code>Derivation</code>.
* @param name is the name of the <code>Derivation</code>.
* @param version is the version of the <code>Derivation</code>.
* @param us is the namespace to search for a <code>Transformation</code>.
* @param uses is the name of the <code>Transformation</code>.
* @param min is the minimum inclusive permissable version.
* @param max is the maximum inclusive permissable version.
* @see Transformation
*/
public Derivation( String ns, String name, String version,
String us, String uses, String min, String max ) {
super(ns,name,version);
this.m_usesspace = us;
this.m_uses = uses;
this.m_minIncludeVersion = min;
this.m_maxIncludeVersion = max;
this.m_passMap = new TreeMap();
}
/**
* Convenience ctor: Names the derivation and supplies the used
* <code>Transformation</code>, and the first actual argument.
*
* @param name is the name of the <code>Derivation</code>.
* @param uses is the name of the <code>Transformation</code>.
* @param firstChild is a first (possibly only) actual argument.
* @see Transformation
* @see Pass
*/
public Derivation( String name, String uses, Pass firstChild ) {
super(name);
this.m_passMap = new TreeMap();
this.m_passMap.put( firstChild.getBind(), firstChild );
this.m_uses = uses;
}
/**
* Accessor: Adds an actual argument to the bag of arguments.
*
* @param vPass is the new actual argument to add.
* @see Pass
*/
public void addPass( Pass vPass )
{
this.m_passMap.put(vPass.getBind(),vPass);
}
/*
* won't work with maps
*
public void addPass( int index, Pass vPass )
throws IndexOutOfBoundsException
{ this.m_passList.insertElementAt(vPass, index); }
*/
/**
* Accessor: Provides an iterator for the bag of actual arguments.
* @return the iterator for <code>Pass</code> elements.
* @see Pass
* @see java.util.Enumeration
* @deprecated Use the new Collection based interfaces
*/
public Enumeration enumeratePass()
{
// return this.m_passMap.elements();
return Collections.enumeration(this.m_passMap.values());
}
/**
* Determines all LFN instances from the actual arguments of a given
* derivation that match the specified linkage. This is a higher-level
* method employing the given interface. Note that also linkage of
* NONE will not be found in wildcard search mode.
*
* @param linkage is the linkage type to match against, -1 for all
* files.
* @return a list of logical filenames from the given derivation which
* match the given linkage. For a linkage of -1, complete LFNs will be
* returned, for any other linkage, just the filename will be returned.
*
* @see Value#getLFNList( int )
* @see LFN
*/
public java.util.List getLFNList( int linkage )
{
java.util.List result = new ArrayList();
for ( Iterator i = this.iteratePass(); i.hasNext() ; ) {
Value value = ((Pass) i.next()).getValue();
result.addAll( value.getLFNList(linkage) );
}
return result;
}
/**
* Determines if the list contains an LFN of the specified linkage.
* The logic uses short-circuit evaluation, thus finding things is
* faster than not finding things. Searching a list is a potentially
* expensive method.
*
* @param filename is the name of the LFN
* @param linkage is the linkage to check for, -1 for any linkage type.
* @return true if the LFN is contained in the scalar, false otherwise.
*
* @see Value#containsLFN( String, int )
* @see LFN
*/
public boolean containsLFN( String filename, int linkage )
{
for ( Iterator i = this.iteratePass(); i.hasNext(); ) {
Value actual = ((Pass) i.next()).getValue();
if ( actual.containsLFN( filename, linkage ) ) return true;
}
return false;
}
/**
* Accessor: Obtains the maximum inclusive version permissable for
* binding to a {@link Transformation}.
*
* @return the maximum inclusive version number.
* @see #setMaxIncludeVersion( java.lang.String )
*/
public String getMaxIncludeVersion()
{
return this.m_maxIncludeVersion;
}
/**
* Accessor: Obtains the minimum inclusive version permissable for
* binding to a {@link Transformation}.
*
* @return the minimum inclusive version number.
* @see #setMinIncludeVersion( java.lang.String )
*/
public String getMinIncludeVersion()
{
return this.m_minIncludeVersion;
}
/**
* Accessor: Obtains an actual argument identified by the bound variable.
*
* @param name is the binding name.
* @return the bound value to the given name.
* @see Pass
*/
public Pass getPass(String name)
{
return (Pass) this.m_passMap.get(name);
}
/**
* Accessor: Obtains the bag of actual arguments as array. Note that the
* order is arbitrary.
*
* @return an array containing all bound variables.
* @see Pass
* @deprecated Use the new Collection based interfaces
*/
public Pass[] getPass()
{
int size = this.m_passMap.size();
Pass[] mPass = new Pass[size];
this.m_passMap.values().toArray(mPass);
return mPass;
}
/**
* Accessor: Counts the number of actual arguments.
*
* @return the number of actual arguments in the internal bag.
*/
public int getPassCount()
{
return this.m_passMap.size();
}
/**
* Accessor: Gets an array of all values that constitute the current
* content. This list is read-only.
*
* @return an array with <code>Pass</code> elements.
* @see Pass
*/
public java.util.List getPassList()
{
return Collections.unmodifiableList( new ArrayList(this.m_passMap.values()) );
}
/**
* Accessor: Obtains all actual arguments. The map is a read-only
* map to avoid modifications outside the API.
*
* @return a map will all actual arguments.
* @see Pass
*/
public java.util.Map getPassMap()
{
return Collections.unmodifiableMap( this.m_passMap );
}
/**
* Accessor: Obtains the name of the logical {@link Transformation}
* that this derivation refers to.
*
* @see #setUses( java.lang.String )
*/
public java.lang.String getUses()
{
return this.m_uses;
}
/**
* Accessor: Obtains the namespace of the logical {@link Transformation}
* that this derivation refers to.
*
* @see #setUsesspace( java.lang.String )
*/
public java.lang.String getUsesspace()
{
return this.m_usesspace;
}
/**
* Accessor: Provides an iterator for the bag of actual arguments.
* @return an iterator to walk the <code>Pass</code> list with.
* @see Pass
*/
public Iterator iteratePass()
{
return this.m_passMap.values().iterator();
}
/* NOT APPLICABLE
*
* Accessor: Provides an iterator for the bag of actual arguments.
* @return an iterator to walk the <code>Pass</code> list with.
* @see Pass
*
public ListIterator listIteratePass()
{
return (new ArrayList( this.m_passMap.values() ).listIterator());
}
*/
/**
* Matches an external version string against the internal range. This
* predicate function uses inclusive matches. Special interpretation
* will be applied to <code>null</code> values, internally as well as
* an external null value.<p>
*
* <pre>
* vers. min max result
* ----- ---- ----- ------
* null null null true
* null * null true
* null null * true
* null * * true
*
* * null null true
*
* "A" "B" null false
* "B" "B" null true
* "C" "B" null true
* "A" null "B" true
* "B" null "B" true
* "C" null "B" false
* "A" "B" "B" false
* "B" "B" "B" true
* "C" "B" "B" false
* </pre>
*
* @param version is an externally supplied version to be checked,
* if it is within the inclusive interval of min and max.
* @param minInc is the minimum inclusive version of the range.
* @param maxInc is the maximum inclusive version of the range.
* @return true, if the version is in range, false otherwise.
*/
public static boolean match( String version, String minInc, String maxInc )
{
// special null combinations first.
if ( minInc == null && maxInc == null || version == null )
return true;
String ver = version.trim();
String min = minInc == null ? "" : minInc;
String max = maxInc == null ? "" : maxInc;
return ( ver.compareTo(min) >= 0 && ver.compareTo(max) <= 0 );
}
/**
* Instance method for matching an external version against the inclusive
* version range.
* @param version is an externally supplied version to be checked,
* if it is within the inclusive interval of min and max.
* @return true, if the version is in range, false otherwise.
* @see Derivation#match( String, String, String )
*/
public boolean match( String version )
{
return Derivation.match( version,
this.m_minIncludeVersion,
this.m_maxIncludeVersion );
}
/**
* Accessor: Removes all actual arguments. Effectively empties the bag.
*/
public void removeAllPass()
{
this.m_passMap.clear();
}
/**
* Accessor: Removes a specific actual argument.
*
* @param name is the bound variable name of the argument to remove.
* @return the object that was removed, or null, if not found.
* @see Pass
*/
public Pass removePass( String name )
{
return (Pass) this.m_passMap.remove(name);
}
/**
* Accessor: Sets the maximum inclusive permissable version of
* a logical transformation to run with.
*
* @param miv is the (new) maximum inclusive version.
* @see #getMaxIncludeVersion()
*/
public void setMaxIncludeVersion(String miv )
{
this.m_maxIncludeVersion = miv == null ? null : miv.trim();
}
/**
* Accessor: Sets the minimum inclusive permissable version of
* a logical transformation to run with.
*
* @param miv is the (new) minimum inclusive version.
* @see #getMinIncludeVersion()
*/
public void setMinIncludeVersion(String miv)
{
this.m_minIncludeVersion = miv == null ? null : miv.trim();
}
/**
* Accessor: Adds a new or overwrites an existing actual argument.
*
* @param vPass is a new actual argument with bound name and value.
* @see Pass
*/
public void setPass(Pass vPass)
{
this.m_passMap.put(vPass.getBind(),vPass);
}
/**
* Accessor: Replaces the bag of actual argument with new arguments.
*
* @param passArray is the new actual argument list.
* @see Pass
* @deprecated Use the new Collection based interfaces
*/
public void setPass(Pass[] passArray)
{
//-- copy array
this.m_passMap.clear();
for (int i = 0; i < passArray.length; i++) {
this.m_passMap.put(passArray[i].getBind(),passArray[i]);
}
}
/**
* Accessor: Replaces the bag of actual argument with a bag of
* new arguments.
*
* @param passes is the new actual argument collection.
* @see Pass
*/
public void setPass(Collection passes)
{
this.m_passMap.clear();
for ( Iterator i=passes.iterator(); i.hasNext(); ) {
Pass p = (Pass) i.next();
this.m_passMap.put(p.getBind(),p);
}
}
/**
* Accessor: Replaces the bag of actual argument with a map of
* new arguments.
*
* @param passes is the new actual argument map.
* @see Pass
*/
public void setPass( Map passes )
{
this.m_passMap.clear();
this.m_passMap.putAll(passes);
}
/**
* Accessor: Sets a new name for a logical <code>Transformation</code>
* to call.
*
* @param uses is the new name of the <code>Transformation</code> to use.
* @see #getUses()
* @see Transformation
*/
public void setUses(String uses)
{
this.m_uses = uses;
}
/**
* Accessor: Sets a new namespace identifier for a logical
* <code>Transformation</code> to call.
*
* @param usesspace is the new namespace of the
* <code>Transformation</code>.
* @see #getUsesspace()
* @see Transformation
*/
public void setUsesspace(String usesspace)
{
this.m_usesspace = usesspace;
}
/**
* Constructs dynamically a short descriptive, hopefully unique
* identifier for this derivation. Recent modification add the
* complete identification in terms of derivation name, namespace,
* and version as well as the called transformation name, namespace
* and version range.
* FIXME: Anonymous derivations get their hash code, which is well
* for the first versions working without database. Later versions
* with database must use some unique sequence mechanism instead.
*
* @return a string describing the derivation
* @see Object#hashCode()
*/
public String identify()
{
StringBuffer result = new StringBuffer();
result.append( shortID() );
result.append("->");
result.append( Separator.combine( this.m_usesspace, this.m_uses,
this.getMinIncludeVersion(),
this.getMaxIncludeVersion() ) );
// // and now for the called part
// result.append( shortID(null, this.m_usesspace, this.m_uses, null) );
//
// String vmin = this.getMinIncludeVersion();
// String vmax = this.getMaxIncludeVersion();
// if ( vmin != null && vmin.length() > 0 &&
// vmax != null && vmax.length() > 0 ) {
// result.append(Separator.NAME);
// if ( vmin != null ) result.append(vmin);
// result.append(Separator.VERSION);
// if ( vmax != null ) result.append(vmax);
// }
// result
return result.toString();
}
/**
* Dumps the content of the given element into a string. This function
* traverses all sibling classes as necessary and converts the
* data into textual output. Note that order of the actual arguments
* is not preserved.
*
* @param stream is a stream opened and ready for writing. This can also
* be a string stream for efficient output.
* @exception IOException if something fishy happens to the stream.
*/
public void toString( Writer stream )
throws IOException
{
String newline = System.getProperty( "line.separator", "\r\n" );
stream.write( "DV " );
stream.write( this.identify() );
stream.write( '(' );
// write arguments
if ( this.m_passMap.size() > 0 ) {
stream.write( newline );
for ( Iterator i=this.m_passMap.values().iterator(); i.hasNext(); ) {
stream.write( " " );
((Pass) i.next()).toString(stream);
if ( i.hasNext() ) stream.write(","+newline);
}
}
stream.write( " );" );
stream.write(newline);
}
/**
* 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, if you use a buffered writer.
*
* @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.
* If a <code>null</code> value is specified, no indentation nor
* linefeeds will be generated.
* @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.
* @see org.griphyn.vdl.Chimera#writeAttribute( Writer, String, String )
* @exception IOException if something fishy happens to the stream.
*/
public void toXML( Writer stream, String indent, String namespace )
throws IOException
{
String newline = System.getProperty( "line.separator", "\r\n" );
String tag = ( namespace != null && namespace.length() > 0 ) ?
namespace + ":derivation" : "derivation";
// open tag
if ( indent != null && indent.length() > 0 ) stream.write( indent );
stream.write( '<' );
stream.write( tag );
super.toXML(stream);
writeAttribute( stream, " usesspace=\"", this.m_usesspace );
writeAttribute( stream, " uses=\"", this.m_uses );
writeAttribute( stream, " minIncludeVersion=\"", this.m_minIncludeVersion );
writeAttribute( stream, " maxIncludeVersion=\"", this.m_maxIncludeVersion );
if ( this.m_passMap.size() == 0 ) {
// no actual arguments
stream.write( "/>" );
} else {
// there are actual arguments
stream.write( '>' );
if ( indent != null ) stream.write( newline );
String newindent = indent==null ? null : indent + " ";
for ( Iterator i=this.m_passMap.values().iterator(); i.hasNext(); ) {
((Pass) i.next()).toXML( stream, newindent, namespace );
}
if ( indent != null && indent.length() > 0 ) stream.write( indent );
stream.write( "</" );
stream.write( tag );
stream.write( '>' );
}
if ( indent != null ) stream.write( newline );
}
}