Index: javax/swing/Timer.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/javax/swing/Timer.java,v retrieving revision 1.3.18.6 diff -u -r1.3.18.6 Timer.java --- javax/swing/Timer.java 17 Jun 2004 20:35:55 -0000 1.3.18.6 +++ javax/swing/Timer.java 30 Jun 2004 06:55:17 -0000 @@ -41,220 +41,436 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.Serializable; +import java.util.Comparator; import java.util.EventListener; - +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; +import javax.swing.SwingUtilities; import javax.swing.event.EventListenerList; +/** + * This timer class is similar to java.util.Timer, except that it only + * uses one thread for all the timers, and timers fire events using swing's + * event dispatch queue. + * The original author is unknown, but I almost rewrote it to conform to + * SUN's javadoc + * + * @author Ka-Hing Cheung + */ public class Timer implements Serializable { - private static final long serialVersionUID = -1116180831621385484L; - protected EventListenerList listenerList = new EventListenerList(); - - // This object manages a "queue" of virtual actionEvents, maintained as a - // simple long counter. When the timer expires, a new event is queued, - // and a dispatcher object is pushed into the system event queue. When - // the system thread runs the dispatcher, it will fire as many - // ActionEvents as have been queued, unless the timer is set to - // coalescing mode, in which case it will fire only one ActionEvent. - - private long queue; - private Object queueLock = new Object(); - private void queueEvent() - { - synchronized (queueLock) - { - queue++; - if (queue == 1) - SwingUtilities.invokeLater(new Runnable() { public void run() { drainEvents(); } }); - } - } - - private void drainEvents() - { - synchronized (queueLock) - { - if (isCoalesce()) - { - if (queue > 0) - fireActionPerformed(); - } - else - { - while(queue > 0) - { - fireActionPerformed(); - queue--; - } - } - queue = 0; - } - } - - static boolean logTimers; - boolean coalesce = true; - boolean repeats = true; - boolean running; - int ticks; - int delay; - int initialDelay; - - private class Waker - extends Thread - { - public void run() - { - running = true; - try - { - - sleep(initialDelay); - - while (running) - { - sleep(delay); - - if (logTimers) - System.out.println("javax.swing.Timer -> clocktick"); - - if (! repeats) - break; - } - running = false; - } - catch (Exception e) - { - System.out.println("swing.Timer::" + e); - } - } - } - - public Timer(int d, ActionListener listener) + /** + * Creates a Timer which invokes the listener at specific intervals + * + * @param delay interval to fire events to listeners + * @param listener default listener + * @see #setInitialDelay + */ + public Timer(int delay, ActionListener listener) { - delay = d; - - if (listener != null) + setDelay(delay); + if(listener != null) addActionListener(listener); } - public void setCoalesce(boolean c) - { - coalesce = c; - } - - public boolean isCoalesce() - { - return coalesce; - } - + /** + * Adds an ActionListener to the list of listeners to invoke + * + * @param listener the ActionListener to add + * @see java.awt.event.ActionListener + */ public void addActionListener(ActionListener listener) { - listenerList.add (ActionListener.class, listener); + listenerList.add(ActionListener.class, listener); } - + + /** + * Removes an ActionListener + * + * @param listener the listener to remove + * @see java.awt.event.ActionListener + */ public void removeActionListener(ActionListener listener) { - listenerList.remove (ActionListener.class, listener); + listenerList.remove(ActionListener.class, listener); } /** + * Gets the listeners that are of a specific type + * + * @param listenerType the type + * @return the array of listeners + * @see #getActionListeners * @since 1.3 */ - public EventListener[] getListeners (Class listenerType) + public EventListener[] getListeners(Class listenerType) { - return listenerList.getListeners (listenerType); + return listenerList.getListeners(listenerType); } /** + * Get all the listeners that are ActionListener + * @return the listeners + * @see #getListeners * @since 1.4 */ - public ActionListener[] getActionListeners () + public ActionListener[] getActionListeners() { return (ActionListener[]) listenerList.getListeners (ActionListener.class); } - protected void fireActionPerformed (ActionEvent event) + /** + * fires the specific event to the listeners + * + * @param event the event to fire + * @see #isCoalesce + * @see #addActionListener + */ + protected void fireActionPerformed(ActionEvent event) { - ActionListener[] listeners = getActionListeners(); + final ActionEvent evt = event; + synchronized(this) + { + final ActionListener[] listeners = getActionListeners(); - for (int i = 0; i < listeners.length; i++) + for (int i = 0; i < listeners.length; i++) + { + final int which = i; + if(!(eventQueued && isCoalesce())) + { + eventQueued = true; + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + listeners[which].actionPerformed(evt); + eventQueued = false; + } + }); + } + } + } + } + + /** + * Restarts the timer + * + * @see #start + * @see #stop + */ + public void restart() + { + synchronized(Timer.class) { - listeners [i].actionPerformed (event); + if(!isRunning()) + { + start(); + } + else + { + running = false; + restart = true; + } } } - void fireActionPerformed () + /** + * Check if this timer repeatly fires. + * + * @return true if this timer repeats + * @see #setRepeats + */ + public boolean isRepeats() + { + return repeat; + } + + /** + * Check if this timer coalesce events + * + * @return true if this timer coalesce events + * @see #setCoalesce + */ + public boolean isCoalesce() + { + return coalesce; + } + + /** + * If true, then this timer coalesce events. This may happen if 1) there are + * too many timers 2) there are too many listeners. In that case an + * ActionListener may receive multiple + * actionPerformed with no delays between them. However, if + * coalesce is true, this will not happen. + * By default, this is true. + * + * @param flag true if this timer should coalesce events + * @see #fireActionPerformed + */ + public void setCoalesce(boolean flag) { - fireActionPerformed (new ActionEvent (this, ticks++, "Timer")); + coalesce = flag; } - public static void setLogTimers(boolean lt) + /** + * If enabled, log some output to the stdout whenever this timer ticks. + * By default, false. + * + * @param flag true if this timer should log whenever it ticks + */ + public static void setLogTimers(boolean flag) { logTimers = lt; } + /** + * Checks if this timer should log whenever it ticks + * + * @return true if this timer logs when it ticks + * @see #setLogTimers + */ public static boolean getLogTimers() { return logTimers; } - - public void setDelay(int d) + + /** + * Sets the time that this timer should wait before firing + * + * @param delay the delay time in millisecond + * @see #setInitialDelay + */ + public void setDelay(int delay) { - delay = d; + interval = delay; } + /** + * Gets the delay time + * + * @return the delay time + * @see #setDelay + */ public int getDelay() { - return delay; + return interval; } - public void setInitialDelay(int i) + /** + * Sets the time that this timer should wait before firing the first time. + * By default this is the same as the regular delay. + * + * @param initialDelay the initial delay + * @see #setDelay + */ + public void setInitialDelay(int initialDelay) { - initialDelay = i; + init_delay = initialDelay; } - public int getInitialDelay() + /** + * Sets if this timer should repeat. By default, true. + * + * @param flag true if this timer should repeatly fires + */ + public void setRepeats(boolean flag) { - return initialDelay; + repeat = flag; } - public void setRepeats(boolean r) + /** + * Checks if this timer is running + * + * @return true if this timer is running + * @see #start + */ + public boolean isRunning() { - repeats = r; + return running; } - public boolean isRepeats() + /** + * Starts this timer if it's not started already. + * + * @see #isRunning + * @see #stop + */ + public void start() { - return repeats; + synchronized(Timer.class) + { + if (!isRunning()) + { + if(init_delay == -1) + init_delay = interval; + + ticks = 0; + running = true; + + putPendingTimer(this); + } + } } - public boolean isRunning() + /** + * Gets the initial delay + * + * @return the initial delay + * @see #setInitialDelay + */ + public int getInitialDelay() { - return running; + return init_delay == -1 ? interval : init_delay; } - public void start() + /** + * Stops the timer if it's running. + * + * @see #isRunning + * @see #start + */ + public void stop() { - if (isRunning()) + synchronized(Timer.class) { - System.err.println("attempt to start a running timer"); - return; + if(isRunning()) + { + running = false; + stopping = true; + } } - new Waker().start(); } - public void restart() + private int ticks = 0; + private static boolean verbose = false; + private boolean running = false; + private boolean repeat = true; + private int interval, init_delay = -1; + private boolean coalesce = true; + + private boolean eventQueued = false; + private boolean restart = false; + private boolean stopping = false; + + private long nextExecTime; + + private static synchronized void putPendingTimer(Timer t) { - synchronized (queueLock) + if(t.ticks == 0) + t.nextExecTime = t.getInitialDelay() + System.currentTimeMillis(); + else + t.nextExecTime = t.getDelay() + System.currentTimeMillis(); + + allTimers.add(t); + + if(waker == null) + { + waker = new Waker(); + waker.start(); + } + else { - queue = 0; + if(allTimers.size() == 1) + Timer.class.notify(); } - start(); } - public void stop() + private static class Waker extends Thread { - running = false; + public void run() + { + while(true) + { + List removedTimers = new LinkedList(); + + synchronized(Timer.class) + { + while(allTimers.isEmpty()) + { + try + { + Timer.class.wait(); + } catch (InterruptedException _) {} + } + + Iterator iter = allTimers.iterator(); + while(iter.hasNext()) + { + Timer t = (Timer)iter.next(); + + if(t.nextExecTime < System.currentTimeMillis()) + { + if(t.running && !t.stopping) + { + if(verbose) + System.out.println("Timer ticked " + t.ticks + + " times."); + + t.fireActionPerformed(new ActionEvent(t, + t.ticks, + "Timer")); + t.ticks++; + if(t.repeat) + { + removedTimers.add(t); + } + else + { + t.ticks = 0; + t.running = false; + } + } + else + { + t.ticks = 0; + if(t.restart) + { + removedTimers.add(t); + t.restart = false; + } + t.stopping = false; + } + iter.remove(); + } + else + { + /* since the TreeSet is sorted, if it's too early to + execute one of the timers, then it's too early to + execute all the subsequent timers + */ + break; + } + } + + iter = removedTimers.iterator(); + while(iter.hasNext()) + { + putPendingTimer((Timer)iter.next()); + } + + } + + try + { + sleep(50); + } + catch (InterruptedException _) {} + } + } } + + private static TreeSet allTimers = new TreeSet(new Comparator() + { + public int compare(Object a, Object b) + { + return (int)(((Timer)a).nextExecTime - ((Timer)b).nextExecTime); + } + }); + + private static Waker waker; }