001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.swing;
027
028 import java.util.*;
029 import java.util.concurrent.atomic.AtomicBoolean;
030 import java.util.concurrent.locks.*;
031 import java.awt.*;
032 import java.awt.event.*;
033 import java.io.Serializable;
034 import javax.swing.event.EventListenerList;
035
036 /**
037 * Fires one or more {@code ActionEvent}s at specified
038 * intervals. An example use is an animation object that uses a
039 * <code>Timer</code> as the trigger for drawing its frames.
040 *<p>
041 * Setting up a timer
042 * involves creating a <code>Timer</code> object,
043 * registering one or more action listeners on it,
044 * and starting the timer using
045 * the <code>start</code> method.
046 * For example,
047 * the following code creates and starts a timer
048 * that fires an action event once per second
049 * (as specified by the first argument to the <code>Timer</code> constructor).
050 * The second argument to the <code>Timer</code> constructor
051 * specifies a listener to receive the timer's action events.
052 *
053 *<pre>
054 * int delay = 1000; //milliseconds
055 * ActionListener taskPerformer = new ActionListener() {
056 * public void actionPerformed(ActionEvent evt) {
057 * <em>//...Perform a task...</em>
058 * }
059 * };
060 * new Timer(delay, taskPerformer).start();</pre>
061 *
062 * <p>
063 * {@code Timers} are constructed by specifying both a delay parameter
064 * and an {@code ActionListener}. The delay parameter is used
065 * to set both the initial delay and the delay between event
066 * firing, in milliseconds. Once the timer has been started,
067 * it waits for the initial delay before firing its
068 * first <code>ActionEvent</code> to registered listeners.
069 * After this first event, it continues to fire events
070 * every time the between-event delay has elapsed, until it
071 * is stopped.
072 * <p>
073 * After construction, the initial delay and the between-event
074 * delay can be changed independently, and additional
075 * <code>ActionListeners</code> may be added.
076 * <p>
077 * If you want the timer to fire only the first time and then stop,
078 * invoke <code>setRepeats(false)</code> on the timer.
079 * <p>
080 * Although all <code>Timer</code>s perform their waiting
081 * using a single, shared thread
082 * (created by the first <code>Timer</code> object that executes),
083 * the action event handlers for <code>Timer</code>s
084 * execute on another thread -- the event-dispatching thread.
085 * This means that the action handlers for <code>Timer</code>s
086 * can safely perform operations on Swing components.
087 * However, it also means that the handlers must execute quickly
088 * to keep the GUI responsive.
089 *
090 * <p>
091 * In v 1.3, another <code>Timer</code> class was added
092 * to the Java platform: <code>java.util.Timer</code>.
093 * Both it and <code>javax.swing.Timer</code>
094 * provide the same basic functionality,
095 * but <code>java.util.Timer</code>
096 * is more general and has more features.
097 * The <code>javax.swing.Timer</code> has two features
098 * that can make it a little easier to use with GUIs.
099 * First, its event handling metaphor is familiar to GUI programmers
100 * and can make dealing with the event-dispatching thread
101 * a bit simpler.
102 * Second, its
103 * automatic thread sharing means that you don't have to
104 * take special steps to avoid spawning
105 * too many threads.
106 * Instead, your timer uses the same thread
107 * used to make cursors blink,
108 * tool tips appear,
109 * and so on.
110 *
111 * <p>
112 * You can find further documentation
113 * and several examples of using timers by visiting
114 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/timer.html"
115 * target = "_top">How to Use Timers</a>,
116 * a section in <em>The Java Tutorial.</em>
117 * For more examples and help in choosing between
118 * this <code>Timer</code> class and
119 * <code>java.util.Timer</code>,
120 * see
121 * <a href="http://java.sun.com/products/jfc/tsc/articles/timer/"
122 * target="_top">Using Timers in Swing Applications</a>,
123 * an article in <em>The Swing Connection.</em>
124 * <p>
125 * <strong>Warning:</strong>
126 * Serialized objects of this class will not be compatible with
127 * future Swing releases. The current serialization support is
128 * appropriate for short term storage or RMI between applications running
129 * the same version of Swing. As of 1.4, support for long term storage
130 * of all JavaBeans<sup><font size="-2">TM</font></sup>
131 * has been added to the <code>java.beans</code> package.
132 * Please see {@link java.beans.XMLEncoder}.
133 *
134 * @see java.util.Timer <code>java.util.Timer</code>
135 *
136 *
137 * @version 1.56 05/05/07
138 * @author Dave Moore
139 */
140 public class Timer implements Serializable {
141 /*
142 * NOTE: all fields need to be handled in readResolve
143 */
144
145 protected EventListenerList listenerList = new EventListenerList();
146
147 // The following field strives to maintain the following:
148 // If coalesce is true, only allow one Runnable to be queued on the
149 // EventQueue and be pending (ie in the process of notifying the
150 // ActionListener). If we didn't do this it would allow for a
151 // situation where the app is taking too long to process the
152 // actionPerformed, and thus we'ld end up queing a bunch of Runnables
153 // and the app would never return: not good. This of course implies
154 // you can get dropped events, but such is life.
155 // notify is used to indicate if the ActionListener can be notified, when
156 // the Runnable is processed if this is true it will notify the listeners.
157 // notify is set to true when the Timer fires and the Runnable is queued.
158 // It will be set to false after notifying the listeners (if coalesce is
159 // true) or if the developer invokes stop.
160 private transient final AtomicBoolean notify = new AtomicBoolean(
161 false);
162
163 private volatile int initialDelay, delay;
164 private volatile boolean repeats = true, coalesce = true;
165
166 private transient final Runnable doPostEvent;
167
168 private static volatile boolean logTimers;
169
170 private transient final Lock lock = new ReentrantLock();
171
172 // This field is maintained by TimerQueue.
173 // eventQueued can also be reset by the TimerQueue, but will only ever
174 // happen in applet case when TimerQueues thread is destroyed.
175 // access to this field is synchronized on getLock() lock.
176 transient TimerQueue.DelayedTimer delayedTimer = null;
177
178 private volatile String actionCommand;
179
180 /**
181 * Creates a {@code Timer} and initializes both the initial delay and
182 * between-event delay to {@code delay} milliseconds. If {@code delay}
183 * is less than or equal to zero, the timer fires as soon as it
184 * is started. If <code>listener</code> is not <code>null</code>,
185 * it's registered as an action listener on the timer.
186 *
187 * @param delay milliseconds for the initial and between-event delay
188 * @param listener an initial listener; can be <code>null</code>
189 *
190 * @see #addActionListener
191 * @see #setInitialDelay
192 * @see #setRepeats
193 */
194 public Timer(int delay, ActionListener listener) {
195 super ();
196 this .delay = delay;
197 this .initialDelay = delay;
198
199 doPostEvent = new DoPostEvent();
200
201 if (listener != null) {
202 addActionListener(listener);
203 }
204 }
205
206 /**
207 * DoPostEvent is a runnable class that fires actionEvents to
208 * the listeners on the EventDispatchThread, via invokeLater.
209 * @see Timer#post
210 */
211 class DoPostEvent implements Runnable {
212 public void run() {
213 if (logTimers) {
214 System.out.println("Timer ringing: " + Timer.this );
215 }
216 if (notify.get()) {
217 fireActionPerformed(new ActionEvent(Timer.this , 0,
218 getActionCommand(), System.currentTimeMillis(),
219 0));
220 if (coalesce) {
221 cancelEvent();
222 }
223 }
224 }
225
226 Timer getTimer() {
227 return Timer.this ;
228 }
229 }
230
231 /**
232 * Adds an action listener to the <code>Timer</code>.
233 *
234 * @param listener the listener to add
235 *
236 * @see #Timer
237 */
238 public void addActionListener(ActionListener listener) {
239 listenerList.add(ActionListener.class, listener);
240 }
241
242 /**
243 * Removes the specified action listener from the <code>Timer</code>.
244 *
245 * @param listener the listener to remove
246 */
247 public void removeActionListener(ActionListener listener) {
248 listenerList.remove(ActionListener.class, listener);
249 }
250
251 /**
252 * Returns an array of all the action listeners registered
253 * on this timer.
254 *
255 * @return all of the timer's <code>ActionListener</code>s or an empty
256 * array if no action listeners are currently registered
257 *
258 * @see #addActionListener
259 * @see #removeActionListener
260 *
261 * @since 1.4
262 */
263 public ActionListener[] getActionListeners() {
264 return (ActionListener[]) listenerList
265 .getListeners(ActionListener.class);
266 }
267
268 /**
269 * Notifies all listeners that have registered interest for
270 * notification on this event type.
271 *
272 * @param e the action event to fire
273 * @see EventListenerList
274 */
275 protected void fireActionPerformed(ActionEvent e) {
276 // Guaranteed to return a non-null array
277 Object[] listeners = listenerList.getListenerList();
278
279 // Process the listeners last to first, notifying
280 // those that are interested in this event
281 for (int i = listeners.length - 2; i >= 0; i -= 2) {
282 if (listeners[i] == ActionListener.class) {
283 ((ActionListener) listeners[i + 1]).actionPerformed(e);
284 }
285 }
286 }
287
288 /**
289 * Returns an array of all the objects currently registered as
290 * <code><em>Foo</em>Listener</code>s
291 * upon this <code>Timer</code>.
292 * <code><em>Foo</em>Listener</code>s
293 * are registered using the <code>add<em>Foo</em>Listener</code> method.
294 * <p>
295 * You can specify the <code>listenerType</code> argument
296 * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
297 * For example, you can query a <code>Timer</code>
298 * instance <code>t</code>
299 * for its action listeners
300 * with the following code:
301 *
302 * <pre>ActionListener[] als = (ActionListener[])(t.getListeners(ActionListener.class));</pre>
303 *
304 * If no such listeners exist,
305 * this method returns an empty array.
306 *
307 * @param listenerType the type of listeners requested;
308 * this parameter should specify an interface
309 * that descends from <code>java.util.EventListener</code>
310 * @return an array of all objects registered as
311 * <code><em>Foo</em>Listener</code>s
312 * on this timer,
313 * or an empty array if no such
314 * listeners have been added
315 * @exception ClassCastException if <code>listenerType</code> doesn't
316 * specify a class or interface that implements
317 * <code>java.util.EventListener</code>
318 *
319 * @see #getActionListeners
320 * @see #addActionListener
321 * @see #removeActionListener
322 *
323 * @since 1.3
324 */
325 public <T extends EventListener> T[] getListeners(
326 Class<T> listenerType) {
327 return listenerList.getListeners(listenerType);
328 }
329
330 /**
331 * Returns the timer queue.
332 */
333 private TimerQueue timerQueue() {
334 return TimerQueue.sharedInstance();
335 }
336
337 /**
338 * Enables or disables the timer log. When enabled, a message
339 * is posted to <code>System.out</code> whenever the timer goes off.
340 *
341 * @param flag <code>true</code> to enable logging
342 * @see #getLogTimers
343 */
344 public static void setLogTimers(boolean flag) {
345 logTimers = flag;
346 }
347
348 /**
349 * Returns <code>true</code> if logging is enabled.
350 *
351 * @return <code>true</code> if logging is enabled; otherwise, false
352 * @see #setLogTimers
353 */
354 public static boolean getLogTimers() {
355 return logTimers;
356 }
357
358 /**
359 * Sets the <code>Timer</code>'s between-event delay, the number of milliseconds
360 * between successive action events. This does not affect the initial delay
361 * property, which can be set by the {@code setInitialDelay} method.
362 *
363 * @param delay the delay in milliseconds
364 * @see #setInitialDelay
365 */
366 public void setDelay(int delay) {
367 if (delay < 0) {
368 throw new IllegalArgumentException("Invalid delay: "
369 + delay);
370 } else {
371 this .delay = delay;
372 }
373 }
374
375 /**
376 * Returns the delay, in milliseconds,
377 * between firings of action events.
378 *
379 * @see #setDelay
380 * @see #getInitialDelay
381 */
382 public int getDelay() {
383 return delay;
384 }
385
386 /**
387 * Sets the <code>Timer</code>'s initial delay, the time
388 * in milliseconds to wait after the timer is started
389 * before firing the first event. Upon construction, this
390 * is set to be the same as the between-event delay,
391 * but then its value is independent and remains unaffected
392 * by changes to the between-event delay.
393 *
394 * @param initialDelay the initial delay, in milliseconds
395 * @see #setDelay
396 */
397 public void setInitialDelay(int initialDelay) {
398 if (initialDelay < 0) {
399 throw new IllegalArgumentException(
400 "Invalid initial delay: " + initialDelay);
401 } else {
402 this .initialDelay = initialDelay;
403 }
404 }
405
406 /**
407 * Returns the <code>Timer</code>'s initial delay.
408 *
409 * @see #setInitialDelay
410 * @see #setDelay
411 */
412 public int getInitialDelay() {
413 return initialDelay;
414 }
415
416 /**
417 * If <code>flag</code> is <code>false</code>,
418 * instructs the <code>Timer</code> to send only one
419 * action event to its listeners.
420 *
421 * @param flag specify <code>false</code> to make the timer
422 * stop after sending its first action event
423 */
424 public void setRepeats(boolean flag) {
425 repeats = flag;
426 }
427
428 /**
429 * Returns <code>true</code> (the default)
430 * if the <code>Timer</code> will send
431 * an action event
432 * to its listeners multiple times.
433 *
434 * @see #setRepeats
435 */
436 public boolean isRepeats() {
437 return repeats;
438 }
439
440 /**
441 * Sets whether the <code>Timer</code> coalesces multiple pending
442 * <code>ActionEvent</code> firings.
443 * A busy application may not be able
444 * to keep up with a <code>Timer</code>'s event generation,
445 * causing multiple
446 * action events to be queued. When processed,
447 * the application sends these events one after the other, causing the
448 * <code>Timer</code>'s listeners to receive a sequence of
449 * events with no delay between them. Coalescing avoids this situation
450 * by reducing multiple pending events to a single event.
451 * <code>Timer</code>s
452 * coalesce events by default.
453 *
454 * @param flag specify <code>false</code> to turn off coalescing
455 */
456 public void setCoalesce(boolean flag) {
457 boolean old = coalesce;
458 coalesce = flag;
459 if (!old && coalesce) {
460 // We must do this as otherwise if the Timer once notified
461 // in !coalese mode notify will be stuck to true and never
462 // become false.
463 cancelEvent();
464 }
465 }
466
467 /**
468 * Returns <code>true</code> if the <code>Timer</code> coalesces
469 * multiple pending action events.
470 *
471 * @see #setCoalesce
472 */
473 public boolean isCoalesce() {
474 return coalesce;
475 }
476
477 /**
478 * Sets the string that will be delivered as the action command
479 * in <code>ActionEvent</code>s fired by this timer.
480 * <code>null</code> is an acceptable value.
481 *
482 * @param command the action command
483 * @since 1.6
484 */
485 public void setActionCommand(String command) {
486 this .actionCommand = command;
487 }
488
489 /**
490 * Returns the string that will be delivered as the action command
491 * in <code>ActionEvent</code>s fired by this timer. May be
492 * <code>null</code>, which is also the default.
493 *
494 * @return the action command used in firing events
495 * @since 1.6
496 */
497 public String getActionCommand() {
498 return actionCommand;
499 }
500
501 /**
502 * Starts the <code>Timer</code>,
503 * causing it to start sending action events
504 * to its listeners.
505 *
506 * @see #stop
507 */
508 public void start() {
509 timerQueue().addTimer(this , getInitialDelay());
510 }
511
512 /**
513 * Returns <code>true</code> if the <code>Timer</code> is running.
514 *
515 * @see #start
516 */
517 public boolean isRunning() {
518 return timerQueue().containsTimer(this );
519 }
520
521 /**
522 * Stops the <code>Timer</code>,
523 * causing it to stop sending action events
524 * to its listeners.
525 *
526 * @see #start
527 */
528 public void stop() {
529 getLock().lock();
530 try {
531 cancelEvent();
532 timerQueue().removeTimer(this );
533 } finally {
534 getLock().unlock();
535 }
536 }
537
538 /**
539 * Restarts the <code>Timer</code>,
540 * canceling any pending firings and causing
541 * it to fire with its initial delay.
542 */
543 public void restart() {
544 getLock().lock();
545 try {
546 stop();
547 start();
548 } finally {
549 getLock().unlock();
550 }
551 }
552
553 /**
554 * Resets the internal state to indicate this Timer shouldn't notify
555 * any of its listeners. This does not stop a repeatable Timer from
556 * firing again, use <code>stop</code> for that.
557 */
558 void cancelEvent() {
559 notify.set(false);
560 }
561
562 void post() {
563 if (notify.compareAndSet(false, true) || !coalesce) {
564 SwingUtilities.invokeLater(doPostEvent);
565 }
566 }
567
568 Lock getLock() {
569 return lock;
570 }
571
572 /*
573 * We have to use readResolve because we can not initialize final
574 * fields for deserialized object otherwise
575 */
576 private Object readResolve() {
577 Timer timer = new Timer(getDelay(), null);
578 timer.listenerList = listenerList;
579 timer.initialDelay = initialDelay;
580 timer.delay = delay;
581 timer.repeats = repeats;
582 timer.coalesce = coalesce;
583 timer.actionCommand = actionCommand;
584 return timer;
585 }
586 }
|