/*
* 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 org.griphyn.vdl.classes.*;
import java.util.*;
import java.io.IOException;
import java.io.Writer;
import java.io.Serializable;
/**
* <code>Use</code> is employed to reference bound actual arguments. Note
* that actual arguments are either of type <code>Scalar</code> or
* of type <code>List</code>. Each argument has a preferred linkage that
* is optionally repeated in this usage class.
*
* <code>Use</code> extends the base class <code>Leaf</code> by adding
* most attributes of all siblings.
*
* @author Jens-S. Vöckler
* @author Yong Zhao
* @version $Revision$
*
* @see Leaf
* @see Text
* @see LFN
*
* @see Value
* @see Scalar
* @see List
*/
public class Use extends Leaf implements Cloneable, Serializable
{
/**
* The linkage type when refering to an argument that contains a filename.
* Legal values range from <code>LFN#NONE</code> to <code>LFN#INOUT</code>.
* The initial value is used to flag the non-initialized state.
*
* @see LFN#NONE
* @see LFN#INPUT
* @see LFN#OUTPUT
* @see LFN#INOUT
* @see LFN#isInRange(int)
*/
private int m_link = -1;
/**
* Stores the name of the bound variable from the actual argument.
* This must not be empty. A value must be filled in to reach a valid
* object state.
*/
private String m_name;
/**
* Stores the prefix string to be used when rendering a <code>List</code>.
* Unused for <code>Scalar</code> content.
*
* @see Value
* @see Scalar
* @see List
*/
private String m_prefix;
/**
* Stores the separator string used when rendering a <code>List</code>.
* Unused for <code>Scalar</code> content.
*
* @see Value
* @see Scalar
* @see List
*/
private String m_separator;
/**
* Stores the suffix string to terminate a <code>List</code> rendering with.
* Unused for <code>Scalar</code> content.
*
* @see Value
* @see Scalar
* @see List
*/
private String m_suffix;
/**
* Creates and returns a copy of this object.
* @return a new instance.
*/
public Object clone()
{
Use result = new Use( this.m_name, this.m_prefix, this.m_separator, this.m_suffix );
result.setAnyLink( this.m_link );
return result;
}
/**
* Default ctor. Creates an empty object that is not valid due
* to the lack of a bound variable name. To be used by the SAX
* parser.
*/
public Use()
{
super();
this.m_separator = " "; // default per XML Schema
this.m_prefix = this.m_suffix = "";
}
/**
* Convenience ctor. Creates an empty object with a bound argument
* name. This ctor should be used by outside applications to assure
* proper initialization of the bound argument name.
*
* @param name is the name of the bound argument to remember.
*/
public Use( String name )
{
super();
this.m_name = name;
this.m_separator = " "; // default per XML Schema
this.m_prefix = this.m_suffix = "";
}
/**
* Convenience ctor. Creates an object with a bound argument name.
* This ctor should be used by outside applications to assure proper
* initialization of the bound argument name.
*
* @param name is the name of the bound argument to remember.
* @param prefix is a prefix when rendering list content into a string.
* @param separator is a string to be placed between list elements when
* rendering a list.
* @param suffix is a suffix when rendering list content into a string.
*
* @see Scalar
* @see List
*/
public Use( String name, String prefix, String separator, String suffix )
{
super();
this.m_name = name;
this.m_prefix = prefix;
this.m_separator = separator;
this.m_suffix = suffix;
}
/**
* Convenience ctor. Creates an object with a bound argument name.
* This ctor should be used by outside applications to assure proper
* initialization of the bound argument name.
*
* @param name is the name of the bound argument to remember.
* @param link is the linkage type of the bound argument for type checking.
* @throws IllegalArgumentException if the linkage is not
* within the legal range between {@link LFN#NONE} and
* {@link LFN#INOUT}.
*/
public Use( String name, int link )
throws IllegalArgumentException
{
super();
this.m_name = name;
this.m_separator = " "; // default per XML Schema
this.m_prefix = this.m_suffix = "";
if ( LFN.isInRange(link) )
this.m_link = link;
else
throw new IllegalArgumentException();
}
/**
* Accessor: Obtains the current state of the linkage.
*
* @return the linkage value. The returned value might be -1 to indicate
* that the linkage was not initialized. Note that -1 is an out of range
* value for linkage.
* @see #setLink(int)
*/
public int getLink()
{
return this.m_link;
}
/**
* Accessor: Obtains the name of the bound actual argument.
*
* @return the bound name. A misconfigured object might return an empty
* or null string.
* @see #setName(String)
*/
public String getName()
{
return this.m_name;
}
/**
* Accessor: Obtains the current prefix rendering information. The
* prefix is used in {@link List} rendering as front bracket.
*
* @return the prefix rendering string, which might be null or empty.
* @see #setPrefix(String)
*/
public String getPrefix()
{
return this.m_prefix;
}
/**
* Accessor: Obtains the current separator rendering information. The
* separator is used in {@link List} rendering as element separator.
*
* @return the separator rendering string, which might be null or empty.
* @see #setSeparator(String)
*/
public String getSeparator()
{
return this.m_separator;
}
/**
* Accessor: Obtains the current suffix rendering information. The
* suffix is used in {@link List} rendering as rear bracket.
*
* @return the suffix rendering string, which might be null or empty.
* @see #setSuffix(String)
*/
public String getSuffix()
{
return this.m_suffix;
}
/**
* Accessor: Sets the linkage of the bound argument.
* @param link is the linkage value as integer within the range.
* @throws IllegalArgumentException if the linkage is not
* within the legal range between {@link LFN#NONE} and
* {@link LFN#INOUT}.
* @see #getLink()
* @see LFN#NONE
* @see LFN#INPUT
* @see LFN#OUTPUT
* @see LFN#INOUT
* @see LFN#isInRange(int)
*/
public void setLink( int link )
throws IllegalArgumentException
{
if ( LFN.isInRange(link) )
this.m_link = link;
else
throw new IllegalArgumentException();
}
private void setAnyLink( int link )
{ this.m_link = link; }
/**
* Accessor: Sets or overwrites the name of the bound argument.
* Do not use empty or null strings here.
*
* @param name is the new variable name to remember.
* @see #getName()
*/
public void setName( String name )
{ this.m_name = name; }
/**
* Accessor: Sets or overwrites the current prefix rendering information.
* The prefix is used in {@link List} rendering as front bracket.
*
* @param prefix is a rendering string, which might be null or empty.
* @see #getPrefix()
*/
public void setPrefix( String prefix )
{
this.m_prefix = prefix;
}
/**
* Accessor: Sets or overwrites the current separator rendering information.
* The separator is used between {@link List} element during rendering.
*
* @param separator is a rendering string, which might be null or empty.
* @see #getSeparator()
*/
public void setSeparator( String separator )
{
this.m_separator = separator;
}
/**
* Accessor: Sets or overwrites the current suffix rendering information.
* The suffix is used in {@link List} rendering as rear bracket.
*
* @param suffix is a rendering string, which might be null or empty.
* @see #getSuffix()
*/
public void setSuffix( String suffix )
{
this.m_suffix = suffix;
}
/**
* Dump content of this instance representation into a stream.<p>
* FIXME: The rendering information is not dumped into the non-XML output.
*
* @param stream is a stream opened and ready for writing. This can
* also be a string stream for efficient output. The stream interface
* should be able to handle large elements efficiently.
* @exception IOException if something fishy happens to the stream.
*/
public void toString( Writer stream )
throws IOException
{
boolean has_fix = ( this.m_prefix != null && this.m_prefix.length() > 0 ||
this.m_suffix != null && this.m_suffix.length() > 0 );
boolean has_sep = ( this.m_separator == null || ! this.m_separator.equals(" ") );
if ( has_fix || has_sep ) {
// must use the tedious version
stream.write( "${" );
if ( has_fix ) {
// this is the ${pre:sep:suf|link:id} version
stream.write('"');
if ( this.m_prefix != null ) stream.write( escape(this.m_prefix) );
stream.write("\":\"");
if ( this.m_separator != null ) stream.write( escape(this.m_separator) );
stream.write("\":\"");
if ( this.m_suffix != null ) stream.write( escape(this.m_suffix) );
stream.write("\"|");
} else if ( has_sep ) {
// this is the ${sep|link:id} version, mind that " " is IMPLIED!
// thus, ${""|link:id} is the output for any null separator, while
// ${link:id} will be the output for a space separator.
stream.write('"');
if ( this.m_separator != null ) stream.write( escape(this.m_separator) );
stream.write("\"|");
}
if ( LFN.isInRange(this.m_link) ) {
stream.write( LFN.toString(this.m_link) ); // no need to escape()
stream.write( ':' );
}
stream.write( escape(this.m_name) );
stream.write( '}' );
} else if ( LFN.isInRange(this.m_link) ) {
// use the type-casting version
stream.write( '(' );
stream.write( LFN.toString(this.m_link) ); // no need to escape()
stream.write( ')' );
stream.write( escape(this.m_name) );
} else {
// can use minimal version
stream.write( escape(this.m_name) );
}
}
/**
* 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.
* @exception IOException if something fishy happens to the stream.
*/
public void toXML( Writer stream, String indent, String namespace )
throws IOException
{
if ( indent != null && indent.length() > 0 ) stream.write( indent );
stream.write( '<' );
if ( namespace != null && namespace.length() > 0 ) {
stream.write( namespace );
stream.write( ':' );
}
stream.write( "use" );
writeAttribute( stream, " name=\"", this.m_name );
if ( LFN.isInRange(this.m_link) )
writeAttribute( stream, " link=\"", LFN.toString(this.m_link) );
if ( this.m_prefix != null && this.m_prefix.length() > 0 )
writeAttribute( stream, " prefix=\"", this.m_prefix );
// If the separator is empty, write it. We may not need to write it,
// if the separator is a space.
if ( this.m_separator == null || ! this.m_separator.equals(" ") ) {
stream.write( " separator=\"" );
if ( this.m_separator != null )
stream.write( quote(this.m_separator,true) );
stream.write( '\"' );
}
if ( this.m_suffix != null && this.m_suffix.length() > 0 )
writeAttribute( stream, " suffix=\"", this.m_suffix ) ;
stream.write( "/>" );
if ( indent != null )
stream.write( System.getProperty( "line.separator", "\r\n" ) );
}
}