/*
* 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 one of the argument types for parameters passed
* to transformations from derivations.
*
* @author Jens-S. Vöckler
* @author Yong Zhao
* @version $Revision$
*
* @see Value
* @see List
*/
public class Scalar extends Value implements Cloneable, Serializable
{
/**
* Any value passed down is an arbitrary mix of the three potential
* {@link Leaf} types.
*
* @see Leaf
* @see Text
* @see LFN
*/
private ArrayList m_leafList;
/**
* Creates and returns a copy of this object.
* @return a new instance.
*/
public Object clone()
{
Scalar result = new Scalar();
for ( int index=0; index < this.m_leafList.size(); ++index ) {
result.addLeaf( (Leaf) this.getLeaf(index).clone() );
}
return result;
}
/**
* Default ctor.
*/
public Scalar()
{
super();
this.m_leafList = new ArrayList();
}
/**
* Convenience ctor: Initializes the object with the first child to
* be put into the list of values.
*
* @param firstChild is either a <code>LFN</code> or <code>Text</code>
* object.
*
* @see Leaf
* @see LFN
* @see Text
*/
public Scalar( Leaf firstChild )
{
super();
this.m_leafList = new ArrayList();
this.m_leafList.add(firstChild);
}
/**
* Accessor: Obtains the value type of this class. By using the abstract
* method in the parent class, <code>Scalar</code> objects can be
* distinguished from <code>List</code> objects without using the
* <code>instanceof</code> operator.
*
* @return the fixed value of being a scalar.
* @see Value#SCALAR
*/
public int getContainerType()
{
// always
return Value.SCALAR;
}
/**
* 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("Scalar");
}
/**
* Accessor: Adds a <code>Leaf</code> value to the list of values gathered
* as the content of a <code>Scalar</code>.
*
* @param vLeaf is the value to append to the list. Note that only leaf
* values of <code>LFN</code> or <code>Text</code> are allowed.
* @throws IndexOutOfBoundsException if the value cannot be added.
* @see Leaf
* @see Text
* @see LFN
*/
public void addLeaf( Leaf vLeaf )
throws IndexOutOfBoundsException
{
this.m_leafList.add(vLeaf);
}
/**
* Accessor: Inserts a <code>Leaf</code> value into a specific position
* of the list of gathered values.
*
* @param index is the position to insert the item into
* @param vLeaf is the value to append to the list. Note that only leaf
* values of <code>LFN</code> or <code>Text</code> are allowed.
* @throws IndexOutOfBoundsException if the value cannot be added.
*
* @see Text
* @see LFN
*/
public void addLeaf( int index, Leaf vLeaf )
throws IndexOutOfBoundsException
{
this.m_leafList.add(index, vLeaf);
}
/**
* Accessor: Enumerates the internal values that constitute the content
* of the <code>Scalar</code> element.
*
* @return an enumeration to walk the list with.
* @deprecated Use the new Collection based interfaces
*/
public Enumeration enumerateLeaf()
{
return Collections.enumeration(this.m_leafList);
}
/**
* 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 LFN
*/
public java.util.List getAllLFN( int linkage )
{
java.util.List result = new ArrayList();
for ( Iterator i = iterateLeaf(); i.hasNext(); ) {
Leaf leaf = (Leaf) i.next();
if ( leaf instanceof LFN ) {
LFN lfn = (LFN) leaf;
if ( linkage == -1 || lfn.getLink() == linkage )
result.add( lfn ); // add *all* information about this file
}
}
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 Derivation#getLFNList( int )
* @see LFN
*/
public java.util.List getLFNList( int linkage )
{
java.util.List result = new ArrayList();
for ( Iterator i = iterateLeaf(); i.hasNext(); ) {
Leaf leaf = (Leaf) i.next();
if ( leaf instanceof LFN ) {
LFN local = (LFN) leaf;
if ( linkage == -1 && local.getLink() != LFN.NONE ) {
result.add( local ); // we need *all* information about this file
} else if ( local.getLink() == linkage ) {
result.add( local.getFilename() ); // we may know some things
}
}
}
return result;
}
/**
* Determines if the scalar contains an LFN of the specified linkage.
* The logic uses short-circuit evaluation, thus finding things is
* faster than not finding things.
*
* @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
*/
public boolean containsLFN( String filename, int linkage )
{
// sanity checks
if ( filename == null )
throw new NullPointerException( "You are searching for a non-existing filename" );
for ( Iterator i = this.iterateLeaf(); i.hasNext(); ) {
Leaf leaf = (Leaf) i.next();
if ( leaf instanceof LFN ) {
int l_link = ((LFN) leaf).getLink();
String l_name = ((LFN) leaf).getFilename();
if ( linkage == -1 && l_link != LFN.NONE ) {
if ( filename.equals(l_name) ) return true;
} else if ( l_link == linkage && filename.equals(l_name) ) {
return true;
}
}
}
// not found
return false;
}
/**
* Accessor: Obtains the <code>Leaf</code> at a certain position in the
* list of leaf values.
*
* @param index is the position in the list to obtain a value from
* @return The <code>LFN</code> or <code>Text</code> at the position.
* @throws IndexOutOfBoundsException if the index points to an elment
* in the list that does not contain any elments.
*
* @see LFN
* @see Text
*/
public Leaf getLeaf(int index)
throws IndexOutOfBoundsException
{
//-- check bound for index
if ((index < 0) || (index >= this.m_leafList.size()))
throw new IndexOutOfBoundsException();
return (Leaf) this.m_leafList.get(index);
}
/**
* Accessor: Gets an array of all values that constitute the current
* content. This array is a copy to avoid write-through modifications.
*
* @return an array with a mixture of either <code>Text</code> or
* <code>LFN</code> values.
*
* @see LFN
* @see Text
* @deprecated Use the new Collection based interfaces
*/
public Leaf[] getLeaf()
{
int size = this.m_leafList.size();
Leaf[] mLeaf = new Leaf[size];
System.arraycopy( this.m_leafList.toArray(new Leaf[0]), 0,
mLeaf, 0, size );
return mLeaf;
}
/**
* Accessor: Obtains the size of the internal list of {@link Leaf}s.
*
* @return number of elements that an external array needs to be sized to.
*/
public int getLeafCount()
{
return this.m_leafList.size();
}
/**
* Accessor: Gets an array of all values that constitute the current
* content. This list is read-only.
*
* @return an array with a mixture of either <code>Text</code> or
* <code>LFN</code> values.
*
* @see LFN
* @see Text
*/
public java.util.List getLeafList()
{
return Collections.unmodifiableList(this.m_leafList);
}
/**
* Accessor: Enumerates the internal values that constitute the content
* of the <code>Scalar</code> element.
*
* @return an iterator to walk the list with.
*/
public Iterator iterateLeaf()
{
return this.m_leafList.iterator();
}
/**
* Accessor: Enumerates the internal values that constitute the content
* of the <code>Scalar</code> element.
*
* @return an iterator to walk the list with.
*/
public ListIterator listIterateLeaf()
{
return this.m_leafList.listIterator();
}
/**
* Accessor: Enumerates the internal values that constitute the content
* of the <code>Scalar</code> element.
*
* @param start is the start index
* @return a list iterator to walk the list with.
*/
public ListIterator listIterateLeaf(int start)
{
return this.m_leafList.listIterator(start);
}
/**
* Accessor: Removes the content of the <code>Scalar</code>.
*/
public void removeAllLeaf()
{
this.m_leafList.clear();
}
/**
* Accessor: Remove a single item from the list of nodes. The list is
* shrunken in the process.
* @param index is the position at which an element is to be removed.
* @return the object that was removed. The object is either a
* <code>LFN</code> or a <code>Text</code>.
*
* @see LFN
* @see Text
*/
public Leaf removeLeaf( int index )
{
return (Leaf) this.m_leafList.remove(index);
}
/**
* Accessor: Overwrites a <code>LFN</code> or <code>Text</code> value
* at a certain position in the content-constituting list.
*
* @param index position to overwrite an elment in.
* @param vLeaf is either a <code>LFN</code> or <code>Text</code> object.
* @throws IndexOutOfBoundsException if the position pointed to is invalid.
*
* @see LFN
* @see Text
*/
public void setLeaf(int index, Leaf vLeaf)
throws IndexOutOfBoundsException
{
//-- check bounds for index
if ((index < 0) || (index >= this.m_leafList.size())) {
throw new IndexOutOfBoundsException();
}
this.m_leafList.set(index, vLeaf);
}
/**
* Accessor: Overwrites internal list with an external list representing
* a <code>Scalar</code> value.
*
* @param leafArray is the external list of <code>Text</code> or
* <code>LFN</code> objects used to overwrite things.
* @see Text
* @see LFN
* @deprecated Use the new Collection based interfaces
*/
public void setLeaf(Leaf[] leafArray)
{
this.m_leafList.clear();
this.m_leafList.addAll( Arrays.asList(leafArray) );
}
/**
* Accessor: Overwrites internal list with an external list representing
* a <code>Scalar</code> value.
*
* @param leaves is the external list of <code>Text</code> or
* <code>LFN</code> objects used to overwrite things.
* @see Text
* @see LFN
*/
public void setLeaf(Collection leaves)
{
this.m_leafList.clear();
this.m_leafList.addAll(leaves);
}
/**
* Converts the object state into textual format for human consumption.
*
* @return a textual description of the element and its sub-classes.
* Be advised that these strings might become large.
*/
public String toString()
{
StringBuffer result = new StringBuffer(40);
for ( Iterator i=this.m_leafList.iterator(); i.hasNext(); ) {
result.append( ((Leaf) i.next()).toString() );
}
return result.toString();
}
/**
* Converts the object state into textual format for human consumption.
*
* @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
{
for ( Iterator i=this.m_leafList.iterator(); i.hasNext(); ) {
((Leaf) i.next()).toString(stream);
}
}
/**
* 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 tag = ( namespace != null && namespace.length() > 0 ) ?
namespace + ":scalar" : "scalar";
String newline = System.getProperty( "line.separator", "\r\n" );
// 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_leafList.iterator(); i.hasNext(); ) {
((Leaf) i.next()).toXML( stream, newindent, namespace );
}
// finalize
if ( indent != null && indent.length() > 0 ) stream.write( indent );
stream.write( "</" );
stream.write( tag );
stream.write( '>' );
if ( indent != null ) stream.write( newline );
}
}