code: package cc.mallet.pipe;
  
  import java.lang.reflect.*;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Iterator;
  import java.util.List;
  import java.io.*;
  
  import cc.mallet.pipe.Pipe;
  import cc.mallet.pipe.iterator.EmptyInstanceIterator;
  import cc.mallet.types.Alphabet;
  import cc.mallet.types.Instance;
  /**
   * Convert an instance through a sequence of pipes.
   */
  
  public class SerialPipes extends Pipe implements Serializable
  {
  	ArrayList<Pipe> pipes;
  
  	public SerialPipes ()
  	{
  		this.pipes = new ArrayList<Pipe> ();
  	}
  	
  	public SerialPipes (Pipe[] pipes)
  	{
  		this.pipes = new ArrayList<Pipe> (pipes.length);
  		for (int i = 0; i < pipes.length; i++)
  			this.pipes.add (pipes[i]);
  		resolveAlphabets();
  	}
  
  	public SerialPipes (Collection<Pipe> pipeList)
  	{
  		pipes = new ArrayList<Pipe> (pipeList);
  		resolveAlphabets();
  	}
  	
  	public abstract class Predicate {
  		public abstract boolean predicate (Pipe p);
  	}
  	
  	public SerialPipes newSerialPipesFromSuffix (Predicate testForStartingNewPipes) {
  		int i = 0;
  		while (i < pipes.size())
  			if (testForStartingNewPipes.predicate(pipes.get(i))) {
  				return new SerialPipes(pipes.subList(i, pipes.size()-1));
  			}
  		throw new IllegalArgumentException ("No pipes in this SerialPipe satisfied starting predicate.");
  	}
  	
  	public SerialPipes newSerialPipesFromRange (int start, int end) {
  		return new SerialPipes(pipes.subList(start, end));
  	}
  	
  	private void resolveAlphabets ()
  	{
  		Alphabet da = null, ta = null;
  		for (Pipe p : pipes) {
  			p.preceedingPipeDataAlphabetNotification(da);
  			da = p.getDataAlphabet();
  			p.preceedingPipeTargetAlphabetNotification(ta);
  			ta = p.getTargetAlphabet();
  		}
  		dataAlphabet = da;
  		targetAlphabet = ta;
  	}
  
  	// protected void add (Pipe pipe)
  	// protected void remove (int i)
  	// This method removed because pipes should be immutable to be safe.
  	// If you need an augmented pipe, you can make a new SerialPipes containing this one.
  
  	public void setTargetProcessing (boolean lookForAndProcessTarget)
  	{
  		super.setTargetProcessing (lookForAndProcessTarget);
  		for (Pipe p : pipes)
  			p.setTargetProcessing (lookForAndProcessTarget);
  	}
  	
  	public Iterator<Instance> newIteratorFrom (Iterator<Instance> source)
  	{
  		if (pipes.size() == 0)
  			return new EmptyInstanceIterator();
  		Iterator<Instance> ret = pipes.get(0).newIteratorFrom(source);
  		for (int i = 1; i < pipes.size(); i++)
  			ret = pipes.get(i).newIteratorFrom(ret);
  		return ret;
  	}
  	
  	public int size()
  	{
  		return pipes.size();
  	}
  
  	public Pipe getPipe (int index) {
  		Pipe retPipe = null;
  		try {
  			retPipe = pipes.get(index);
  		}
  		catch (Exception e) {
  			System.err.println("Error getting pipe. Index = " + index + ".  " + e.getMessage());
  		}
  		return retPipe;
  	}
  	
  	/** Allows access to the underlying collection of Pipes.  Use with caution. */
  	public ArrayList<Pipe> pipes() {
  		return pipes;
  	}
  	
  	public String toString ()
  	{
  		StringBuffer sb = new StringBuffer();
  		for (Pipe p : pipes)
  			sb.append (p.toString()+",");
  		return sb.toString();
  	}
  
  	// Serialization 
  	
  	private static final long serialVersionUID = 1;
  	private static final int CURRENT_SERIAL_VERSION = 0;
  	
  	private void writeObject (ObjectOutputStream out) throws IOException {
  		out.writeInt (CURRENT_SERIAL_VERSION);
  		out.writeObject(pipes);
  	}
  	
  	private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException {
  		int version = in.readInt ();
  		pipes = (ArrayList) in.readObject();
  		resolveAlphabets();
  	}
  
  }
  