001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package org.wings.util;
014:
015: import org.apache.commons.logging.Log;
016: import org.apache.commons.logging.LogFactory;
017:
018: import java.awt.event.ActionEvent;
019: import java.awt.event.ActionListener;
020: import java.io.Serializable;
021: import java.util.Vector;
022:
023: /*
024: * Die Klasse ist praktisch von der Swing Implementierung
025: * abgeleitet. Leider brauch ich einen Timer, der auch innerhalb des
026: * Swing Event Threads Impulse gibt. Deshalb angepasst. Ich hoffe das
027: * gibt keine Problemen mit dem Swing Team.
028: */
029:
030: /**
031: * @author <a href="mailto:haaf@mercatis.de">Armin Haaf</a>
032: */
033: public final class Timer implements Serializable {
034: private final static Log log = LogFactory.getLog(Timer.class);
035:
036: /*
037: * Die Verzoegerung, bis das erste mal ein Impuls (ActionEvent)
038: * kommt.
039: */
040: private long initialDelay;
041:
042: /*
043: * Die Dauer zwischen 2 Impulsen (ActionEvent)
044: */
045: private long delay;
046:
047: /*
048: * Einmaliger Impuls, oder andauernder.
049: */
050: private boolean repeats = true;
051:
052: /*
053: * Sollen Impulse, die zur gleichen Zeit auflaufen (etwa weil der
054: * {@link TimerQueue} so lange geschlafen hat, bis mehrere Impulse vom
055: * gleichen Timer aufgelaufen sind) sind zusammengefasst und nur einer
056: * geschickt werden ?
057: */
058: private boolean coalesce = true;
059:
060: /*
061: * Alle ActionListener, die zu benachrichtigen sind.
062: */
063: private final Vector listenerList = new Vector();
064:
065: boolean eventQueued = false;
066:
067: /**
068: * Command used when action is fired.
069: */
070: private String actionCommand = null;
071:
072: /*
073: * Sollen Impulse geloggt werden.
074: */
075: private static boolean logTimers;
076:
077: // These fields are maintained by TimerQueue.
078: // eventQueued can also be reset by the TimerQueue, but will only ever
079: // happen in applet case when TimerQueues thread is destroyed.
080: long expirationTime;
081: Timer nextTimer;
082: boolean running;
083:
084: /**
085: * Creates a Timer that will notify its listeners every
086: * <i>delay</i> milliseconds.
087: *
088: * @param delay The number of milliseconds between listener notification
089: * @param listener An initial listener
090: * @see #setInitialDelay
091: * @see #setRepeats
092: */
093: public Timer(long delay, ActionListener listener) {
094: super ();
095: this .delay = delay;
096: this .initialDelay = delay;
097:
098: addActionListener(listener);
099: }
100:
101: /**
102: * Adds an actionListener to the Timer
103: */
104: public void addActionListener(ActionListener listener) {
105: listenerList.addElement(listener);
106: }
107:
108: /**
109: * Removes an ActionListener from the Timer.
110: */
111: public void removeActionListener(ActionListener listener) {
112: listenerList.removeElement(listener);
113: }
114:
115: /**
116: * Sets action command for this timer.
117: */
118: public void setActionCommand(String command) {
119: actionCommand = command;
120: }
121:
122: /**
123: * Notify all listeners that have registered interest for
124: * notification on this event type. The event instance
125: * is lazily created using the parameters passed into
126: * the fire method.
127: */
128: protected void fireActionPerformed(ActionEvent e) {
129: // Process the listeners last to first, notifying
130: // those that are interested in this event
131: for (int i = listenerList.size() - 1; i >= 0; i--) {
132: ((ActionListener) listenerList.elementAt(i))
133: .actionPerformed(e);
134: }
135: }
136:
137: /**
138: * Returns the timer queue.
139: */
140: TimerQueue timerQueue() {
141: return TimerQueue.sharedInstance();
142: }
143:
144: /**
145: * Enables or disables the timer log. When enabled, a message
146: * is posted to System.out whenever the timer goes off.
147: *
148: * @param flag true to enable logging
149: * @see #getLogTimers
150: */
151: public static void setLogTimers(boolean flag) {
152: logTimers = flag;
153: }
154:
155: /**
156: * Returns true if logging is enabled.
157: *
158: * @return true if logging is enabled
159: * @see #setLogTimers
160: */
161: public static boolean getLogTimers() {
162: return logTimers;
163: }
164:
165: /**
166: * Sets the Timer's delay, the number of milliseconds between successive
167: * <b>actionPerfomed()</b> messages to its listeners
168: *
169: * @see #setInitialDelay
170: */
171: public void setDelay(long delay) {
172: TimerQueue queue;
173:
174: if (delay < 0) {
175: throw new RuntimeException("Invalid initial delay: "
176: + delay);
177: }
178: this .delay = delay;
179:
180: if (isRunning()) {
181: queue = timerQueue();
182: queue.removeTimer(this );
183: cancelEvent();
184: queue.addTimer(this , System.currentTimeMillis() + delay);
185: }
186: }
187:
188: /**
189: * Returns the Timer's delay.
190: *
191: * @see #setDelay
192: */
193: public long getDelay() {
194: return delay;
195: }
196:
197: /**
198: * Sets the Timer's initial delay. This will be used for the first
199: * "ringing" of the Timer only. Subsequent ringings will be spaced
200: * using the delay property.
201: *
202: * @see #setDelay
203: */
204: public void setInitialDelay(int initialDelay) {
205: if (initialDelay < 0) {
206: throw new RuntimeException("Invalid initial delay: "
207: + initialDelay);
208: }
209: this .initialDelay = initialDelay;
210: }
211:
212: /**
213: * Returns the Timer's initial delay.
214: *
215: * @see #setDelay
216: */
217: public long getInitialDelay() {
218: return initialDelay;
219: }
220:
221: /**
222: * If <b>flag</b> is <b>false</b>, instructs the Timer to send
223: * <b>actionPerformed()</b> to its listeners only once, and then stop.
224: */
225: public void setRepeats(boolean flag) {
226: repeats = flag;
227: }
228:
229: /**
230: * Returns <b>true</b> if the Timer will send a <b>actionPerformed()</b>
231: * message to its listeners multiple times.
232: *
233: * @see #setRepeats
234: */
235: public boolean isRepeats() {
236: return repeats;
237: }
238:
239: /**
240: * Sets whether the Timer coalesces multiple pending ActionEvent firings.
241: * A busy application may not be able
242: * to keep up with a Timer's message generation, causing multiple
243: * <b>actionPerformed()</b> message sends to be queued. When processed,
244: * the application sends these messages one after the other, causing the
245: * Timer's listeners to receive a sequence of <b>actionPerformed()</b>
246: * messages with no delay between them. Coalescing avoids this situation
247: * by reducing multiple pending messages to a single message send. Timers
248: * coalesce their message sends by default.
249: */
250: public void setCoalesce(boolean flag) {
251: coalesce = flag;
252: }
253:
254: /**
255: * Returns <b>true</b> if the Timer coalesces multiple pending
256: * <b>performCommand()</b> messages.
257: *
258: * @see #setCoalesce
259: */
260: public boolean isCoalesce() {
261: return coalesce;
262: }
263:
264: /**
265: * Starts the Timer, causing it to send <b>actionPerformed()</b> messages
266: * to its listeners.
267: *
268: * @see #stop
269: */
270: public void start() {
271: timerQueue().addTimer(this ,
272: System.currentTimeMillis() + getInitialDelay());
273: }
274:
275: /**
276: * Returns <b>true</b> if the Timer is running.
277: *
278: * @see #start
279: */
280: public boolean isRunning() {
281: return timerQueue().containsTimer(this );
282: }
283:
284: /**
285: * Stops a Timer, causing it to stop sending <b>actionPerformed()</b>
286: * messages to its Target.
287: *
288: * @see #start
289: */
290: public void stop() {
291: timerQueue().removeTimer(this );
292: cancelEvent();
293: }
294:
295: /**
296: * Restarts a Timer, canceling any pending firings, and causing
297: * it to fire with its initial dely.
298: */
299: public void restart() {
300: stop();
301: start();
302: }
303:
304: synchronized void cancelEvent() {
305: eventQueued = false;
306: }
307:
308: synchronized void post() {
309: if (eventQueued == false) {
310: eventQueued = true;
311: if (logTimers) {
312: log.debug("Timer ringing: " + Timer.this );
313: }
314: if (eventQueued) {
315: fireActionPerformed(new ActionEvent(Timer.this , 0,
316: this.actionCommand));
317: cancelEvent();
318: }
319: }
320: }
321: }
|