/*
* 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;
/**
* This class implements the list argument type used for parameters passed
* to transformations from derivations.
*
* @author Jens-S. Vöckler
* @author Yong Zhao
* @version $Revision$
*
* @see Value
* @see Scalar
*/
public class List extends Value implements Cloneable, Serializable
{
/**
* A list is just an ordered bunch of {@link Scalar}.
*/
private ArrayList m_scalarList;
/**
* Creates and returns a copy of this object.
* @return a new instance.
*/
public Object clone()
{
List result = new List();
for ( int index=0; index < this.m_scalarList.size(); ++index ) {
result.addScalar( (Scalar) this.getScalar(index).clone() );
}
return result;
}
/**
* Default ctor.
*/
public List()
{
super();
this.m_scalarList = new ArrayList();
}
/**
* Convenience ctor: Initializes the list, and stores the given
* {@link Scalar} as first child into the list.
*
* @param firstChild is the first element in the list
*/
public List( Scalar firstChild )
{
super();
this.m_scalarList = new ArrayList();
this.m_scalarList.add(firstChild);
}
/**
* Accessor: Obtains the value type of this class. By using the abstract
* method in the parent class, <code>List</code> objects can be
* distinguished from <code>Scalar</code> objects without using the
* <code>instanceof</code> operator.
*
* @return the fixed value of being a scalar.
* @see Value#LIST
*/
public int getContainerType()
{
return Value.LIST;
}
/**
* This method determines which container is being used in the abstract
* base class in order to kludgy statements when printing debug info.
*
* @return the symblic identifier for the type of the Value.
*/
public String getSymbolicType()
{
// always
return new String("List");
}
/**
* Accessor: Appends as <code>Scalar</code> value to the list.
*
* @param vScalar is the <code>Scalar</code> to append to the list.
* @throws IndexOutOfBoundsException if the value cannot be added.
* @see Scalar
*/
public void addScalar( Scalar vScalar )
throws IndexOutOfBoundsException
{
this.m_scalarList.add(vScalar);
}
/**
* Accessor: Insert a <code>Scalar</code> at a specific position.
*
* @param index is the position to insert the item into
* @param vScalar is the <code>Scalar</code> to append to the list.
* @throws IndexOutOfBoundsException if the value cannot be added.
* @see Scalar
*/
public void addScalar( int index, Scalar vScalar )
throws IndexOutOfBoundsException
{
this.m_scalarList.add(index, vScalar);
}
/**
* Accessor: constructs the iterator for the <code>List</code> items.
*
* @return an enumeration to walk the list with.
* @deprecated Use the new Collection based interfaces
*/
public Enumeration enumerateScalar()
{
return Collections.enumeration(this.m_scalarList);
}
/**
* Determines all LFN instances of a given scalar that match the
* specified linkage. This is a higher-level method employing the
* given API.
*
* @param linkage is the linkage to check for, -1 for all filenames.
* @return a set of logical filename instances that match the linkage
* and were part of the scalar. The result may be an empty set, if no
* such result were to be found.
*
* @see Scalar#getAllLFN( int )
* @see LFN
*/
public java.util.List getAllLFN( int linkage )
{
java.util.List result = new ArrayList();
for ( Iterator i=this.iterateScalar(); i.hasNext(); )
result.addAll( ((Scalar) i.next()).getAllLFN(linkage) );
return result;
}
/**
* Determines all LFN instances of a given scalar that match the
* specified linkage. This is a higher-level method employing the
* given API. Note that also linkage of NONE will not be found in
* wildcard search mode.
*
* @param linkage is the linkage to check for, -1 for all filenames.
* @return a set of all logical filenames that match the linkage and
* were part of the scalar. The result may be an empty set, if no such
* result were to be found. For a linkage of -1, complete LFNs will be
* returned, for any other linkage, just the filename will be
* returned.
*
* @see Scalar#getLFNList( int )
* @see Derivation#getLFNList( int )
* @see LFN
*/
public java.util.List getLFNList( int linkage )
{
java.util.List result = new ArrayList();
for ( Iterator i=this.iterateScalar(); i.hasNext(); )
result.addAll( ((Scalar) i.next()).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 org.griphyn.vdl.classes.LFN
* @see Scalar#containsLFN( String, int )
*/
public boolean containsLFN( String filename, int linkage )
{
for ( Iterator i=this.iterateScalar(); i.hasNext(); )
if ( ((Scalar) i.next()).containsLFN( filename, linkage ) )
return true;
// not found
return false;
}
/**
* Accessor: Obtains the value of a specific item in the list.
*
* @param index is the position of which to obtain the value of.
* @return The {@link Scalar} at the specified position.
* @throws IndexOutOfBoundsException if the index points to an element
* that is beyond the list boundaries.
*/
public Scalar getScalar(int index)
throws IndexOutOfBoundsException
{
//-- check bound for index
if ((index < 0) || (index >= this.m_scalarList.size()))
throw new IndexOutOfBoundsException();
return (Scalar) this.m_scalarList.get(index);
}
/**
* Accessor: Gets an array of all <code>Scalar</code>s in the list.
*
* @return an array of <code>Scalar</code>s.
* @see Scalar
* @deprecated Use the new Collection based interfaces
*/
public Scalar[] getScalar()
{
int size = this.m_scalarList.size();
Scalar[] mScalar = new Scalar[size];
System.arraycopy( this.m_scalarList.toArray( new Scalar[0] ), 0,
mScalar, 0, size );
return mScalar;
}
/**
* Accessor: Obtains the element count of the internal list
*
* @return number of elements in the internal list
*/
public int getScalarCount()
{ return this.m_scalarList.size(); }
/**
* Accessor: Gets an array of all <code>Scalar</code>s in the list.
* This list is read-only.
*
* @return an array of <code>Scalar</code>s.
* @see Scalar
*/
public java.util.List getScalarList()
{
return Collections.unmodifiableList(this.m_scalarList);
}
/**
* Accessor: constructs the iterator for the <code>List</code> items.
*
* @return an enumeration to walk the list with.
*/
public Iterator iterateScalar()
{
return this.m_scalarList.iterator();
}
/**
* Accessor: constructs the iterator for the <code>List</code> items.
*
* @return an enumeration to walk the list with.
*/
public ListIterator listIterateScalar()
{
return this.m_scalarList.listIterator();
}
/**
* Accessor: constructs the iterator for the <code>List</code> items.
* @param start is the starting position for the sub-iteration.
* @return an enumeration to walk the list with.
*/
public ListIterator listIterateScalar( int start )
{
return this.m_scalarList.listIterator(start);
}
/**
* Accessor: Removes all elements in the <code>List</code>.
*/
public void removeAllScalar()
{
this.m_scalarList.clear();
}
/**
* Accessor: Removes a single element from the <code>List</code>. Each
* component in this vector with an index greater or equal to the
* specified index is shifted downward to have an index one smaller
* than the value it had previously. The size of this vector is
* decreased by 1.
*
* @param index is the position at which an element is to be removed.
* @return the {@link Scalar} that was removed.
* @throws ArrayIndexOutOfBoundsException if the index was invalid.
*/
public Scalar removeScalar( int index )
{
return (Scalar) this.m_scalarList.remove(index);
}
/**
* Accessor: Overwrite an element at a given position.
*
* @param index is the position to use. It must be within the list.
* @param vScalar is the new value to replace the element with.
* @throws IndexOutOfBoundsException if the position is outside the list.
*/
public void setScalar(int index, Scalar vScalar)
throws IndexOutOfBoundsException
{
//-- check bounds for index
if ((index < 0) || (index >= this.m_scalarList.size())) {
throw new IndexOutOfBoundsException();
}
this.m_scalarList.set(index, vScalar);
} //-- void setScalar(int, Scalar)
/**
* Accessor: Replaces all elements with a new list of {@link Scalar}s.
*
* @param scalarArray is the list to replace the original list with.
* @deprecated Use the new Collection based interfaces
*/
public void setScalar(Scalar[] scalarArray)
{
//-- copy array
this.m_scalarList.clear();
this.m_scalarList.addAll( Arrays.asList(scalarArray) );
}
/**
* Accessor: Replaces all elements with a new list of {@link Scalar}s.
*
* @param scalars is the list to replace the original list with.
*/
public void setScalar(Collection scalars)
{
this.m_scalarList.clear();
this.m_scalarList.addAll( scalars );
}
/**
* Dumps the list and all its contents into a string. The list will
* be terminated by brackets, elements separated by komma, space.
* Elements itself will be dumped by recursive calls to the element
* specific method of the same name.
*
* @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
{
stream.write( "[ " );
for ( Iterator i=this.m_scalarList.iterator(); i.hasNext(); ) {
((Scalar) i.next()).toString(stream);
if ( i.hasNext() ) stream.write( ", " );
}
stream.write( " ]" );
}
/**
* 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.
* @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
{
String newline = System.getProperty( "line.separator", "\r\n" );
String tag = ( namespace != null && namespace.length() > 0 ) ?
namespace + ":list" : "list";
// open tag
if ( indent != null && indent.length() > 0 ) stream.write( indent );
stream.write( '<' );
stream.write( tag );
stream.write( '>' );
if ( indent != null ) stream.write( newline );
// dump content
String newindent = indent==null ? null : indent+" ";
for ( Iterator i=this.m_scalarList.iterator(); i.hasNext(); ) {
// FIXME: If we cast to Value, we can have lists in lists
((Scalar) i.next()).toXML( stream, newindent, namespace );
}
// close tag
if ( indent != null && indent.length() > 0 ) stream.write( indent );
stream.write( "</" );
stream.write( tag );
stream.write( '>' );
if ( indent != null ) stream.write( newline );
}
}