/*
 * CycWorker.java
 *
 * Created on March 21, 2002, 7:00 PM
 */
package org.opencyc.util;

import java.util.*;
import javax.swing.event.EventListenerList;

/**
 *
 * This is a convenient event callback wrapper around the
 * the SwingWorker class. It's inteded to allow easily
 * running tasks in the background. Users of this class
 * will be notified when the worker is started, interrupted
 * or finished with its work. If finished, the rusults of the
 * work will be made available as well.
 * <P>
 * Here is some example code:<P>
 * <CODE><PLAINTEXT>
 *   public static CycWorker evalSubLInBackground(CycAccess conn,
 *                                                String subl,
 *                                                CycWorkerListener cwl) {
 *       CycWorker worker = new CycWorker() {
 *           //The return val of construct() method is considered to be
 *           //the output for this worker and retrieved with
 *           //worker.getWork().
 *           public Object construct() { 
 *               return evalSubL(conn, subl); //Do work here.
 *           }
 *       };
 *       if(cwl != null) { worker.addListener(cwl); }
 *       worker.start();
 *       return worker;
 *   }
 *
 *   public class GKETermDialog implements CycWorkerListener {
 *
 *      private CycWorker specWorker = null;
 *
 *	public void notifyWorkerStarted(CycWorkerEvent evt) {}
 *    
 *	public void notifyWorkerInterrupted(CycWorkerEvent evt) {}
 *    
 *	public void notifyWorkerFinished(CycWorkerEvent evt) {
 *	    CycWorker worker = (CycWorker)evt.getSource();
 *	    System.out.println("WORKER FINISHED: " + worker);
 *	    System.out.println("WORKER OUTPUT: " + worker.getWork());
 *	}
 *   }
 * </PLAINTEXT></CODE>
 * <p>
 * THIS SOFTWARE AND KNOWLEDGE BASE CONTENT ARE PROVIDED ``AS IS'' AND
 * ANY EXPRESSED 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 OPENCYC
 * ORGANIZATION OR ITS CONTRIBUTORS 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 AND KNOWLEDGE
 * BASE CONTENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * @see CycWorkerListener
 * @see CycWorkerEvent
 * @see SwingWorker
 * @author tbrussea
 * @version $Id: CycWorker.java,v 1.1 2002/05/15 21:54:14 stephenreed Exp $
 */
public abstract class CycWorker extends SwingWorker {

  /** This CycWorkers listeners (instances of CycWorkerListener only).**/
  private EventListenerList listeners = new EventListenerList();
    
  /** Creates a new instance of CycWorker */
  public CycWorker() { }
    
  /**
   * Adds a new worker listener to this worker. Listeners will
   * be notified when the worker is started, interrupted or 
   * finished.
   * @param cwl The CycWorkerListener that wishes to listen to
   * this worker
   **/
  public void addListener(CycWorkerListener cwl) {
    listeners.add(CycWorkerListener.class, cwl);
  }
  
  /**
   * Removes a worker listener from this worker.
   * @param cwl The CycWorkerListener that no longer wishes to
   * be notified of this workers events.
   */
  public void removeListener(CycWorkerListener cwl) {
    listeners.remove(CycWorkerListener.class, cwl);
  }
  
  /**
   * Return a list of all CycWorkerListeners listening to
   * this CycWorker.
   * @return A non-null array of CycWorkerListener objects.
   */
  public Object[] getListeners() {
    return listeners.getListeners(CycWorkerListener.class);
  }
    
  final private static int CYC_WORKER_START=0;
  final private static int CYC_WORKER_INTERRUPT=1;
  final private static int CYC_WORKER_FINISHED=2;
    
  private void notifyStatChange(int eventType) {
    try {
      Object[] curListeners = listeners.getListenerList();
      CycWorkerEvent cwe = null;
      for (int i = curListeners.length-2; i>=0; i-=2) {
	if (curListeners[i]==CycWorkerListener.class) {
	  if (cwe == null) { cwe = new CycWorkerEvent(this); }
	  switch(eventType) {
	  case CYC_WORKER_START:
	    ((CycWorkerListener)curListeners[i+1]).
	      notifyWorkerStarted(cwe);
	    break;
	  case CYC_WORKER_INTERRUPT:
	    ((CycWorkerListener)curListeners[i+1]).
	      notifyWorkerInterrupted(cwe);
	    break;
	  case CYC_WORKER_FINISHED:
	    ((CycWorkerListener)curListeners[i+1]).
	      notifyWorkerFinished(cwe);
	    break;
	  }
	}
      }
    } catch (Exception e) { e.printStackTrace(); }
  }
  
  /** 
   * This method starts the backround processing of a particular
   * CycWorker. Appropriate listeners are notified when this
   * is called.
   */
  public void start() {
    notifyStatChange(CYC_WORKER_START);
    super.start();
  }
      
  /** 
   * This method interrupts a currently running CycWorker.
   * Appropriate listeners are notified when this is called.
   */       
  public void interrupt() {
    notifyStatChange(CYC_WORKER_INTERRUPT);
    super.interrupt();
  }
  
  /** 
   * This method should be called internally only...public for
   * Java related reasons only. It is automatically called
   * then the construct() method finishes.
   * Appropriate listeners are notified when this is called.
   */
  public void finished() {
    notifyStatChange(CYC_WORKER_FINISHED);
    super.finished();
  }
  
  /**
   * Exposed the get() method from SwingWorker class.
   * Returns the result of this CycWorker. Returns
   * null if worker not started or worker interrupted before 
   * processing could be completed. If processing is ongoing,
   * will block till finished.
   * @return Returns the result of this CycWorker or null if
   * CycWorker interrupted or not started. 
   **/
  public Object getWork() { return super.get(); }

}
