/*______________________________________________________________________________
 * 
 * net.innig.util.ThreadFlock
 * 
 *______________________________________________________________________________
 * 
 * Copyright 2001 Kendall Helmstetter Gelner, Paul Cantrell
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * (1) Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 * (2) Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution. 
 *
 * (3) The name of the author may not be used to endorse or promote
 *     products derived from this software without specific prior
 *     written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *_______________________________________________________________________________
 */

package net.innig.util;

import java.util.*;

/**
    Creates a collection of threads running the same {@link Runnable}.
    The threads have the same priority and the same daemon status.
    You can start, interrupt, and join the threads en masse.
    
    <p align="center">
    <table cellpadding=4 cellspacing=2 border=0 bgcolor="#338833" width="90%"><tr><td bgcolor="#EEEEEE">
        <b>Maturity:</b>
        This is a moderately mature API, and a stable implementation.
        It's worked well in a number of informal tests, but has not been through methodical
        or real-world testing.
    </td></tr><tr><td bgcolor="#EEEEEE">
        <b>Plans:</b>
        Write methodical tests.  Try it in a real-world application.  Fix stuff.
    </td></tr></table>

    @author Paul Cantrell
    @version [Development version]
*/

public class ThreadFlock
    {
    /** Creates a new flock with one thread.
     *  To start the threads, use {@link #start()}. */
    
    public ThreadFlock(Runnable runnable)
        {
        this(runnable, 1);
        }
    
    /** Creates a new flock with an arbitrary number of threads.
     *  To start the threads, use {@link #start()}. */
    
    public ThreadFlock(Runnable runnable, int threadCount)
        {
        this.runnable = runnable;
        threads = new ArrayList();
        setPriority(Thread.NORM_PRIORITY);
        setThreadCount(threadCount, false);
        }
    
    /** Creates a new flock with one thread.
     *  The flock takes the priority and daemon status of the thread parameter,
     *  but that thread does not actually become a member of the flock.
     *  To start the threads, use {@link #start()}. */
    
    public ThreadFlock(Thread thread)
        {
        this(thread, 1);
        }
    
    /** Creates a new flock with an arbitrary number of threads.
     *  The flock takes the priority and daemon status of the thread parameter,
     *  but that thread does not actually become a member of the flock.
     *  To start the threads, use {@link #start()}. */
    
    public ThreadFlock(Thread thread, int threadCount)
        {
        this((Runnable) thread, threadCount);
        setDaemon(thread.isDaemon());
        setPriority(thread.getPriority());
        }
    
    /** Returns the number of threads in this flock. */
    
    public int getThreadCount()
        {
        return threads.size();
        }
    
    /** Sets the number of threads in this flock.
     *  If this is an increase, this method will create the new
     *  threads, and will start them if the start parameter is true.  If this is a
     *  decrease, this method will {@link java.lang.Thread#interrupt() interrupt}
     *  the appropriate number of threads. */
    
    public void setThreadCount(int threadCount, boolean start)
        {
        if(threadCount < 0)
            throw new IllegalArgumentException("threadCount < 0");
        
        int oldCount = threads.size();
        if(threadCount == oldCount)
            return;
        
        for(int n = threadCount; n < oldCount; n++)
            {
            ((Thread) threads.get(n)).interrupt();
            threads.remove(n);
            }
        
        for(int n = oldCount; n < threadCount; n++)
            {
            Thread t = new Thread(runnable);
            threads.add(t);
            if(start)
                t.start();
            }
        
        setPriority(priority);
        }
    
    /** Returns the priority of the threads in this flock.  Note that each
     *  individual thread has this priority, so a flock with 50 threads of normal
     *  priority is actually an extremely high-priority flock.
     *  @see java.lang.Thread#getPriority() */
    
    public int getPriority()
        {
        return priority;
        }
    
    /** Sets the priority of all the threads in this flock.  This affects
     *  threads whether or not they are running, and affects any threads
     *  created by subsequent calls to {@link #setThreadCount(int,boolean) setThreadCount}.
     *  @see java.lang.Thread#setPriority(int) */
    
    public void setPriority(int priority)
        {
        this.priority = priority;
        for(int n = 0; n < threads.size(); n++)
            ((Thread) threads.get(n)).setPriority(priority);
        }
    
    /** Determines whether the threads in this flock are daemons.
     *  (Daemon threads die automatically when the Java VM goes away.)
     *  @see java.lang.Thread#isDaemon() */
    
    public boolean isDaemon()
        {
        return daemon;
        }
    
    /** Changes whether the threads in this flock are daemons.  This affects
     *  threads whether or not they are running, and affects any threads
     *  created by subsequent calls to {@link #setThreadCount(int,boolean) setThreadCount}.
     *  @see java.lang.Thread#setDaemon(boolean) */
    
    public void setDaemon(boolean daemon)
        {
        this.daemon = daemon;
        for(int n = 0; n < threads.size(); n++)
            ((Thread) threads.get(n)).setDaemon(daemon);
        }
    
    /** Starts all the threads in the flock.
     *  @see java.lang.Thread#start() */
    
    public void start()
        {
        for(int n = 0; n < threads.size(); n++)
            ((Thread) threads.get(n)).start();
        }
    
    /** Interrupts all the threads in the flock.
     *  @see java.lang.Thread#interrupt() */
    
    public void interrupt()
        {
        for(int n = 0; n < threads.size(); n++)
            ((Thread) threads.get(n)).interrupt();
        }
    
    /** Waits for all the threads in the flock to die.
     *  @see java.lang.Thread#join(long) */
    
    public void join(long time)
        throws InterruptedException
        {
        for(int n = 0; n < threads.size(); n++)
            ((Thread) threads.get(n)).join(time);
        }
    
    private Runnable runnable;
    private ArrayList threads;
    private int priority;
    private boolean daemon;
    }



