/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License");  you may not use this file except in 
 * compliance with the License.  You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is Protege-2000.
 *
 * The Initial Developer of the Original Code is Stanford University. Portions
 * created by Stanford University are Copyright (C) 2007.  All Rights Reserved.
 *
 * Protege was developed by Stanford Medical Informatics
 * (http://www.smi.stanford.edu) at the Stanford University School of Medicine
 * with support from the National Library of Medicine, the National Science
 * Foundation, and the Defense Advanced Research Projects Agency.  Current
 * information about Protege can be obtained at http://protege.stanford.edu.
 *
 */

package edu.stanford.smi.protege.util;

import java.util.logging.Level;

/**
 * This class is a Runnable that only allows one thread to be executing at a time.
 * It includes methods that allow a caller to wait for the execution to start or finish.
 * This class implements its own notions of running and aborted.
 * 
 * Is this implemented in Java Core?
 * 
 * @author tredmond
 *
 */
public abstract class ExclusiveRunnable implements Runnable {
  private boolean running = false;
  private boolean aborted = false;
  private Object lock = new Object();
  
  /**
   * This method is where the developer implements his own execute procedure.
   *
   */
  public abstract void execute();

  /**
   * It is generally expected that the developer will not override this method.  
   * The actual code to execute should be placed in the execute() method.
   */
  public void run() {
    synchronized (lock) {
      waitForShutdown();
      running = true;
      aborted = false;
      lock.notifyAll();
    }
    execute();
    synchronized (lock) {
      running = false;
      lock.notifyAll();
    }
  }
  
  /**
   * Waits for the execution to start.
   *
   */
  public void waitForStartup() {
    synchronized (lock) {
      while (!running) {
        try {
          lock.wait();
        } catch (InterruptedException e) {
          Log.getLogger().log(Level.WARNING,"Unexpected interrupt", e);
        }       
      }
    }
  }
  
  /**
   * Wait for execution to complete.
   *
   */
  public void waitForShutdown() {
    synchronized (lock) {
      while (running) {
        try {
          lock.wait();
        } catch (InterruptedException e) {
          Log.getLogger().log(Level.WARNING,"Unexpected interrupt", e);
        }
      }
    }
  }
  
  /**
   * Sets the aborted flag for the current runnable.
   * 
   * If you don't wait for the runnable to be running then setting the aborted
   * flag won't do what you want.  It is possible to start a thread (but it isn't
   * running yet), abort  the thread (still not running) and for the thread to start
   * running with the abort flag reset to false.
   *
   */
  public void abort() {
    synchronized (lock) {
      aborted = true;
    }
  }
  
  public boolean isAborted() {
    synchronized (lock) {
      return aborted;
    }
  }
}
