001: /*
002: * $Id: BaseWatchable.java,v 1.2 2007/12/20 18:17:41 rbair Exp $
003: *
004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005: * Santa Clara, California 95054, U.S.A. All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020: */
021:
022: package com.sun.pdfview;
023:
024: /**
025: * An abstract implementation of the watchable interface, that is extended
026: * by the parser and renderer to do their thing.
027: */
028: public abstract class BaseWatchable implements Watchable, Runnable {
029: /** the current status, from the list in Watchable */
030: private int status = Watchable.UNKNOWN;
031:
032: /** a lock for status-related operations */
033: private Object statusLock = new Object();
034:
035: /** a lock for parsing operations */
036: private Object parserLock = new Object();
037:
038: /** when to stop */
039: private Gate gate;
040:
041: /** the thread we are running in */
042: private Thread thread;
043:
044: /**
045: * Creates a new instance of BaseWatchable
046: */
047: protected BaseWatchable() {
048: setStatus(Watchable.NOT_STARTED);
049: }
050:
051: /**
052: * Perform a single iteration of this watchable. This is the minimum
053: * granularity which the go() commands operate over.
054: *
055: * @return one of three values: <ul>
056: * <li> Watchable.RUNNING if there is still data to be processed
057: * <li> Watchable.NEEDS_DATA if there is no data to be processed but
058: * the execution is not yet complete
059: * <li> Watchable.COMPLETED if the execution is complete
060: * </ul>
061: */
062: protected abstract int iterate() throws Exception;
063:
064: /**
065: * Prepare for a set of iterations. Called before the first iterate() call
066: * in a sequence. Subclasses should extend this method if they need to do
067: * anything to setup.
068: */
069: protected void setup() {
070: // do nothing
071: }
072:
073: /**
074: * Clean up after a set of iterations. Called after iteration has stopped
075: * due to completion, manual stopping, or error.
076: */
077: protected void cleanup() {
078: // do nothing
079: }
080:
081: public void run() {
082: // System.out.println(Thread.currentThread().getName() + " starting");
083:
084: // call setup once we started
085: if (getStatus() == Watchable.NOT_STARTED) {
086: setup();
087: }
088:
089: setStatus(Watchable.PAUSED);
090:
091: synchronized (parserLock) {
092: while (!isFinished() && getStatus() != Watchable.STOPPED) {
093: if (isExecutable()) {
094: // set the status to running
095: setStatus(Watchable.RUNNING);
096:
097: try {
098: // keep going until the status is no longer running,
099: // our gate tells us to stop, or no-one is watching
100: while ((getStatus() == Watchable.RUNNING)
101: && (gate == null || !gate.iterate())) {
102: // update the status based on this iteration
103: setStatus(iterate());
104: }
105:
106: // make sure we are paused
107: if (getStatus() == Watchable.RUNNING) {
108: setStatus(Watchable.PAUSED);
109: }
110: } catch (Exception ex) {
111: setError(ex);
112: }
113: } else {
114: // System.out.println(getName() + " waiting: status = " + getStatusString());
115: // wait for our status to change
116: synchronized (statusLock) {
117: if (!isExecutable()) {
118: try {
119: statusLock.wait();
120: } catch (InterruptedException ie) {
121: // ignore
122: }
123: }
124: }
125: }
126: }
127: }
128:
129: // System.out.println(Thread.currentThread().getName() + " exiting: status = " + getStatusString());
130:
131: // call cleanup when we are done
132: if (getStatus() == Watchable.COMPLETED
133: || getStatus() == Watchable.ERROR) {
134:
135: cleanup();
136: }
137:
138: // notify that we are no longer running
139: thread = null;
140: }
141:
142: /**
143: * Get the status of this watchable
144: *
145: * @return one of the well-known statuses
146: */
147: public int getStatus() {
148: return status;
149: }
150:
151: /**
152: * Return whether this watchable has finished. A watchable is finished
153: * when its status is either COMPLETED, STOPPED or ERROR
154: */
155: public boolean isFinished() {
156: int s = getStatus();
157: return (s == Watchable.COMPLETED || s == Watchable.ERROR);
158: }
159:
160: /**
161: * return true if this watchable is ready to be executed
162: */
163: public boolean isExecutable() {
164: return ((status == Watchable.PAUSED || status == Watchable.RUNNING) && (gate == null || !gate
165: .stop()));
166: }
167:
168: /**
169: * Stop this watchable. Stop will cause all processing to cease,
170: * and the watchable to be destroyed.
171: */
172: public void stop() {
173: setStatus(Watchable.STOPPED);
174: }
175:
176: /**
177: * Start this watchable and run in a new thread until it is finished or
178: * stopped.
179: * Note the watchable may be stopped if go() with a
180: * different time is called during execution.
181: */
182: public synchronized void go() {
183: gate = null;
184:
185: execute(false);
186: }
187:
188: /**
189: * Start this watchable and run until it is finished or stopped.
190: * Note the watchable may be stopped if go() with a
191: * different time is called during execution.
192: *
193: * @param synchronous if true, run in this thread
194: */
195: public synchronized void go(boolean synchronous) {
196: gate = null;
197:
198: execute(synchronous);
199: }
200:
201: /**
202: * Start this watchable and run for the given number of steps or until
203: * finished or stopped.
204: *
205: * @param steps the number of steps to run for
206: */
207: public synchronized void go(int steps) {
208: gate = new Gate();
209: gate.setStopIterations(steps);
210:
211: execute(false);
212: }
213:
214: /**
215: * Start this watchable and run for the given amount of time, or until
216: * finished or stopped.
217: *
218: * @param millis the number of milliseconds to run for
219: */
220: public synchronized void go(long millis) {
221: gate = new Gate();
222: gate.setStopTime(millis);
223:
224: execute(false);
225: }
226:
227: /**
228: * Wait for this watchable to finish
229: */
230: public void waitForFinish() {
231: synchronized (statusLock) {
232: while (!isFinished() && getStatus() != Watchable.STOPPED) {
233: try {
234: statusLock.wait();
235: } catch (InterruptedException ex) {
236: // ignore
237: }
238: }
239: }
240: }
241:
242: /**
243: * Start executing this watchable
244: *
245: * @param synchronous if true, run in this thread
246: */
247: protected synchronized void execute(boolean synchronous) {
248: // see if we're already running
249: if (thread != null) {
250: // we're already running. Make sure we wake up on any change.
251: synchronized (statusLock) {
252: statusLock.notifyAll();
253: }
254:
255: return;
256: } else if (isFinished()) {
257: // we're all finished
258: return;
259: }
260:
261: // we'return not running. Start up
262: if (synchronous) {
263: thread = Thread.currentThread();
264: run();
265: } else {
266: thread = new Thread(this );
267: thread.start();
268: }
269: }
270:
271: /**
272: * Set the status of this watchable
273: */
274: protected void setStatus(int status) {
275: synchronized (statusLock) {
276: this .status = status;
277:
278: // System.out.println(getName() + " status set to " + getStatusString());
279:
280: statusLock.notifyAll();
281: }
282: }
283:
284: /**
285: * Set an error on this watchable
286: */
287: protected void setError(Exception error) {
288: error.printStackTrace();
289:
290: setStatus(Watchable.ERROR);
291: }
292:
293: private String getStatusString() {
294: switch (getStatus()) {
295: case Watchable.NOT_STARTED:
296: return "Not started";
297: case Watchable.RUNNING:
298: return "Running";
299: case Watchable.NEEDS_DATA:
300: return "Needs Data";
301: case Watchable.PAUSED:
302: return "Paused";
303: case Watchable.STOPPED:
304: return "Stopped";
305: case Watchable.COMPLETED:
306: return "Completed";
307: case Watchable.ERROR:
308: return "Error";
309: default:
310: return "Unknown";
311:
312: }
313: }
314:
315: /** A class that lets us give it a target time or number of steps,
316: * and will tell us to stop after that much time or that many steps
317: */
318: class Gate {
319: /** whether this is a time-based (true) or step-based (false) gate */
320: private boolean timeBased;
321: /** the next gate, whether time or iterations */
322: private long nextGate;
323:
324: /** set the stop time */
325: public void setStopTime(long millisFromNow) {
326: timeBased = true;
327: nextGate = System.currentTimeMillis() + millisFromNow;
328: }
329:
330: /** set the number of iterations until we stop */
331: public void setStopIterations(int iterations) {
332: timeBased = false;
333: nextGate = iterations;
334: }
335:
336: /** check whether we should stop.
337: */
338: public boolean stop() {
339: if (timeBased) {
340: return (System.currentTimeMillis() >= nextGate);
341: } else {
342: return (nextGate < 0);
343: }
344: }
345:
346: /** Notify the gate of one iteration. Returns true if we should
347: * stop or false if not
348: */
349: public boolean iterate() {
350: if (!timeBased) {
351: nextGate--;
352: }
353:
354: return stop();
355: }
356: }
357: }
|