379 lines
12 KiB
Java
379 lines
12 KiB
Java
/*
|
|
* $Id: BaseWatchable.java,v 1.5 2009/02/09 17:14:32 tomoke Exp $
|
|
*
|
|
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
|
|
* Santa Clara, California 95054, U.S.A. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
package com.sun.pdfview;
|
|
|
|
/**
|
|
* An abstract implementation of the watchable interface, that is extended
|
|
* by the parser and renderer to do their thing.
|
|
*/
|
|
public abstract class BaseWatchable implements Watchable, Runnable {
|
|
|
|
/** the current status, from the list in Watchable */
|
|
private int status = Watchable.UNKNOWN;
|
|
/** a lock for status-related operations */
|
|
private Object statusLock = new Object();
|
|
/** a lock for parsing operations */
|
|
private Object parserLock = new Object();
|
|
/** when to stop */
|
|
private Gate gate;
|
|
/** suppress local stack trace on setError. */
|
|
private static boolean SuppressSetErrorStackTrace = false;
|
|
/** the thread we are running in */
|
|
private Thread thread;
|
|
|
|
/**
|
|
* Creates a new instance of BaseWatchable
|
|
*/
|
|
protected BaseWatchable() {
|
|
setStatus(Watchable.NOT_STARTED);
|
|
}
|
|
|
|
/**
|
|
* Perform a single iteration of this watchable. This is the minimum
|
|
* granularity which the go() commands operate over.
|
|
*
|
|
* @return one of three values: <ul>
|
|
* <li> Watchable.RUNNING if there is still data to be processed
|
|
* <li> Watchable.NEEDS_DATA if there is no data to be processed but
|
|
* the execution is not yet complete
|
|
* <li> Watchable.COMPLETED if the execution is complete
|
|
* </ul>
|
|
*/
|
|
protected abstract int iterate() throws Exception;
|
|
|
|
/**
|
|
* Prepare for a set of iterations. Called before the first iterate() call
|
|
* in a sequence. Subclasses should extend this method if they need to do
|
|
* anything to setup.
|
|
*/
|
|
protected void setup() {
|
|
// do nothing
|
|
}
|
|
|
|
/**
|
|
* Clean up after a set of iterations. Called after iteration has stopped
|
|
* due to completion, manual stopping, or error.
|
|
*/
|
|
protected void cleanup() {
|
|
// do nothing
|
|
}
|
|
|
|
public void run() {
|
|
// System.out.println(Thread.currentThread().getName() + " starting");
|
|
|
|
// call setup once we started
|
|
if (getStatus() == Watchable.NOT_STARTED) {
|
|
setup();
|
|
}
|
|
|
|
setStatus(Watchable.PAUSED);
|
|
|
|
synchronized (parserLock) {
|
|
while (!isFinished() && getStatus() != Watchable.STOPPED) {
|
|
if (isExecutable()) {
|
|
// set the status to running
|
|
setStatus(Watchable.RUNNING);
|
|
|
|
try {
|
|
// keep going until the status is no longer running,
|
|
// our gate tells us to stop, or no-one is watching
|
|
while ((getStatus() == Watchable.RUNNING) &&
|
|
(gate == null || !gate.iterate())) {
|
|
// update the status based on this iteration
|
|
setStatus(iterate());
|
|
}
|
|
|
|
// make sure we are paused
|
|
if (getStatus() == Watchable.RUNNING) {
|
|
setStatus(Watchable.PAUSED);
|
|
}
|
|
} catch (Exception ex) {
|
|
setError(ex);
|
|
}
|
|
} else {
|
|
// System.out.println(getName() + " waiting: status = " + getStatusString());
|
|
// wait for our status to change
|
|
synchronized (statusLock) {
|
|
if (!isExecutable()) {
|
|
try {
|
|
statusLock.wait();
|
|
} catch (InterruptedException ie) {
|
|
// ignore
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// System.out.println(Thread.currentThread().getName() + " exiting: status = " + getStatusString());
|
|
|
|
// call cleanup when we are done
|
|
if (getStatus() == Watchable.COMPLETED ||
|
|
getStatus() == Watchable.ERROR) {
|
|
|
|
cleanup();
|
|
}
|
|
|
|
// notify that we are no longer running
|
|
thread = null;
|
|
}
|
|
|
|
/**
|
|
* Get the status of this watchable
|
|
*
|
|
* @return one of the well-known statuses
|
|
*/
|
|
public int getStatus() {
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Return whether this watchable has finished. A watchable is finished
|
|
* when its status is either COMPLETED, STOPPED or ERROR
|
|
*/
|
|
public boolean isFinished() {
|
|
int s = getStatus();
|
|
return (s == Watchable.COMPLETED ||
|
|
s == Watchable.ERROR);
|
|
}
|
|
|
|
/**
|
|
* return true if this watchable is ready to be executed
|
|
*/
|
|
public boolean isExecutable() {
|
|
return ((status == Watchable.PAUSED || status == Watchable.RUNNING) &&
|
|
(gate == null || !gate.stop()));
|
|
}
|
|
|
|
/**
|
|
* Stop this watchable. Stop will cause all processing to cease,
|
|
* and the watchable to be destroyed.
|
|
*/
|
|
public void stop() {
|
|
setStatus(Watchable.STOPPED);
|
|
}
|
|
|
|
/**
|
|
* Start this watchable and run in a new thread until it is finished or
|
|
* stopped.
|
|
* Note the watchable may be stopped if go() with a
|
|
* different time is called during execution.
|
|
*/
|
|
public synchronized void go() {
|
|
gate = null;
|
|
|
|
execute(false);
|
|
}
|
|
|
|
/**
|
|
* Start this watchable and run until it is finished or stopped.
|
|
* Note the watchable may be stopped if go() with a
|
|
* different time is called during execution.
|
|
*
|
|
* @param synchronous if true, run in this thread
|
|
*/
|
|
public synchronized void go(boolean synchronous) {
|
|
gate = null;
|
|
|
|
execute(synchronous);
|
|
}
|
|
|
|
/**
|
|
* Start this watchable and run for the given number of steps or until
|
|
* finished or stopped.
|
|
*
|
|
* @param steps the number of steps to run for
|
|
*/
|
|
public synchronized void go(int steps) {
|
|
gate = new Gate();
|
|
gate.setStopIterations(steps);
|
|
|
|
execute(false);
|
|
}
|
|
|
|
/**
|
|
* Start this watchable and run for the given amount of time, or until
|
|
* finished or stopped.
|
|
*
|
|
* @param millis the number of milliseconds to run for
|
|
*/
|
|
public synchronized void go(long millis) {
|
|
gate = new Gate();
|
|
gate.setStopTime(millis);
|
|
|
|
execute(false);
|
|
}
|
|
|
|
/**
|
|
* Wait for this watchable to finish
|
|
*/
|
|
public void waitForFinish() {
|
|
synchronized (statusLock) {
|
|
while (!isFinished() && getStatus() != Watchable.STOPPED) {
|
|
try {
|
|
statusLock.wait();
|
|
} catch (InterruptedException ex) {
|
|
// ignore
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start executing this watchable
|
|
*
|
|
* @param synchronous if true, run in this thread
|
|
*/
|
|
protected synchronized void execute(boolean synchronous) {
|
|
// see if we're already running
|
|
if (thread != null) {
|
|
// we're already running. Make sure we wake up on any change.
|
|
synchronized (statusLock) {
|
|
statusLock.notifyAll();
|
|
}
|
|
|
|
return;
|
|
} else if (isFinished()) {
|
|
// we're all finished
|
|
return;
|
|
}
|
|
|
|
// we'return not running. Start up
|
|
if (synchronous) {
|
|
thread = Thread.currentThread();
|
|
run();
|
|
} else {
|
|
thread = new Thread(this);
|
|
thread.setName(getClass().getName());
|
|
thread.start();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the status of this watchable
|
|
*/
|
|
protected void setStatus(int status) {
|
|
synchronized (statusLock) {
|
|
this.status = status;
|
|
|
|
// System.out.println(getName() + " status set to " + getStatusString());
|
|
|
|
statusLock.notifyAll();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* return true if we would be suppressing setError stack traces.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public static boolean isSuppressSetErrorStackTrace () {
|
|
return SuppressSetErrorStackTrace;
|
|
}
|
|
|
|
/**
|
|
* set suppression of stack traces from setError.
|
|
*
|
|
* @param suppressTrace
|
|
*/
|
|
public static void setSuppressSetErrorStackTrace(boolean suppressTrace) {
|
|
SuppressSetErrorStackTrace = suppressTrace;
|
|
}
|
|
|
|
/**
|
|
* Set an error on this watchable
|
|
*/
|
|
protected void setError(Exception error) {
|
|
if (!SuppressSetErrorStackTrace) {
|
|
error.printStackTrace();
|
|
}
|
|
|
|
setStatus(Watchable.ERROR);
|
|
}
|
|
|
|
private String getStatusString() {
|
|
switch (getStatus()) {
|
|
case Watchable.NOT_STARTED:
|
|
return "Not started";
|
|
case Watchable.RUNNING:
|
|
return "Running";
|
|
case Watchable.NEEDS_DATA:
|
|
return "Needs Data";
|
|
case Watchable.PAUSED:
|
|
return "Paused";
|
|
case Watchable.STOPPED:
|
|
return "Stopped";
|
|
case Watchable.COMPLETED:
|
|
return "Completed";
|
|
case Watchable.ERROR:
|
|
return "Error";
|
|
default:
|
|
return "Unknown";
|
|
|
|
}
|
|
}
|
|
|
|
/** A class that lets us give it a target time or number of steps,
|
|
* and will tell us to stop after that much time or that many steps
|
|
*/
|
|
class Gate {
|
|
|
|
/** whether this is a time-based (true) or step-based (false) gate */
|
|
private boolean timeBased;
|
|
/** the next gate, whether time or iterations */
|
|
private long nextGate;
|
|
|
|
/** set the stop time */
|
|
public void setStopTime(long millisFromNow) {
|
|
timeBased = true;
|
|
nextGate = System.currentTimeMillis() + millisFromNow;
|
|
}
|
|
|
|
/** set the number of iterations until we stop */
|
|
public void setStopIterations(int iterations) {
|
|
timeBased = false;
|
|
nextGate = iterations;
|
|
}
|
|
|
|
/** check whether we should stop.
|
|
*/
|
|
public boolean stop() {
|
|
if (timeBased) {
|
|
return (System.currentTimeMillis() >= nextGate);
|
|
} else {
|
|
return (nextGate < 0);
|
|
}
|
|
}
|
|
|
|
/** Notify the gate of one iteration. Returns true if we should
|
|
* stop or false if not
|
|
*/
|
|
public boolean iterate() {
|
|
if (!timeBased) {
|
|
nextGate--;
|
|
}
|
|
|
|
return stop();
|
|
}
|
|
}
|
|
}
|