Index: javax/swing/Timer.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/Timer.java,v
retrieving revision 1.10
diff -u -r1.10 Timer.java
--- javax/swing/Timer.java 31 Jul 2004 15:24:02 -0000 1.10
+++ javax/swing/Timer.java 22 Aug 2004 22:57:24 -0000
@@ -1,4 +1,4 @@
-/* Timer.java --
+/* Timer.java --
Copyright (C) 2002, 2004 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -35,186 +35,45 @@
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
+
package javax.swing;
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;
-
-/**
- * DOCUMENT ME!
- */
public class Timer implements Serializable
{
- /** DOCUMENT ME! */
- private static final long serialVersionUID = -1116180831621385484L;
-
- /** DOCUMENT ME! */
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.
-
- /** DOCUMENT ME! */
- private long queue;
-
- /** DOCUMENT ME! */
- private Object queueLock = new Object();
-
- /** DOCUMENT ME! */
- private Waker waker;
-
- /**
- * DOCUMENT ME!
- */
- private void queueEvent()
- {
- synchronized (queueLock)
- {
- queue++;
- if (queue == 1)
- SwingUtilities.invokeLater(new Runnable()
- {
- public void run()
- {
- drainEvents();
- }
- });
-
- }
- }
-
- /**
- * DOCUMENT ME!
- */
- private void drainEvents()
- {
- synchronized (queueLock)
- {
- if (isCoalesce())
- {
- if (queue > 0)
- fireActionPerformed();
- }
- else
- {
- while (queue > 0)
- {
- fireActionPerformed();
- queue--;
- }
- }
- queue = 0;
- }
- }
-
- static boolean logTimers;
-
- /** DOCUMENT ME! */
- boolean coalesce = true;
-
- /** DOCUMENT ME! */
- boolean repeats = true;
-
- /** DOCUMENT ME! */
- boolean running;
-
- /** DOCUMENT ME! */
- int ticks;
-
- /** DOCUMENT ME! */
- int delay;
-
- /** DOCUMENT ME! */
- int initialDelay;
-
- /**
- * DOCUMENT ME!
- */
- private class Waker extends Thread
- {
- /**
- * DOCUMENT ME!
- */
- public void run()
- {
- running = true;
- try
- {
- sleep(initialDelay);
-
- while (running)
- {
- try
- {
- sleep(delay);
- }
- catch (InterruptedException e)
- {
- return;
- }
- queueEvent();
-
- if (logTimers)
- System.out.println("javax.swing.Timer -> clocktick");
-
- if (! repeats)
- break;
- }
- running = false;
- }
- catch (Exception e)
- {
- System.out.println("swing.Timer::" + e);
- }
- }
- }
-
/**
- * Creates a new Timer object.
+ * Creates a Timer which invokes the listener at specific intervals
*
- * @param d DOCUMENT ME!
- * @param listener DOCUMENT ME!
+ * @param delay interval to fire events to listeners
+ * @param listener default listener
+ * @see #setInitialDelay
*/
- public Timer(int d, ActionListener listener)
+ public Timer(int delay, ActionListener listener)
{
- delay = d;
-
- if (listener != null)
+ setDelay(delay);
+ if(listener != null)
addActionListener(listener);
}
/**
- * DOCUMENT ME!
- *
- * @param c DOCUMENT ME!
- */
- public void setCoalesce(boolean c)
- {
- coalesce = c;
- }
-
- /**
- * DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- */
- public boolean isCoalesce()
- {
- return coalesce;
- }
-
- /**
- * DOCUMENT ME!
+ * Adds an ActionListener
to the list of listeners to invoke
*
- * @param listener DOCUMENT ME!
+ * @param listener the ActionListener
to add
+ * @see java.awt.event.ActionListener
*/
public void addActionListener(ActionListener listener)
{
@@ -222,9 +81,10 @@
}
/**
- * DOCUMENT ME!
+ * Removes an ActionListener
*
- * @param listener DOCUMENT ME!
+ * @param listener the listener to remove
+ * @see java.awt.event.ActionListener
*/
public void removeActionListener(ActionListener listener)
{
@@ -232,136 +92,193 @@
}
/**
- * DOCUMENT ME!
- *
- * @param listenerType DOCUMENT ME!
- *
- * @return DOCUMENT ME!
+ * 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)
{
return listenerList.getListeners(listenerType);
}
-
+
/**
- * DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- *
+ * Get all the listeners that are ActionListener
+ * @return the listeners
+ * @see #getListeners
* @since 1.4
*/
public ActionListener[] getActionListeners()
{
- return (ActionListener[]) listenerList.getListeners(ActionListener.class);
+ return (ActionListener[]) listenerList.getListeners (ActionListener.class);
}
/**
- * DOCUMENT ME!
+ * fires the specific event to the listeners
*
- * @param event DOCUMENT ME!
+ * @param event the event to fire
+ * @see #isCoalesce
+ * @see #addActionListener
*/
protected void fireActionPerformed(ActionEvent event)
{
- ActionListener[] listeners = getActionListeners();
+ final ActionEvent evt = event;
+ final ActionListener[] listeners = getActionListeners();
- for (int i = 0; i < listeners.length; i++)
- listeners[i].actionPerformed(event);
+ synchronized(this)
+ {
+ 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;
+ }
+ });
+ }
+ }
+ }
}
/**
- * DOCUMENT ME!
+ * Restarts the timer
+ *
+ * @see #start
+ * @see #stop
*/
- void fireActionPerformed()
+ public void restart()
{
- fireActionPerformed(new ActionEvent(this, ticks++, "Timer"));
+ synchronized(Timer.class)
+ {
+ if(!isRunning())
+ {
+ start();
+ }
+ else
+ {
+ stop();
+ start();
+ }
+ }
}
/**
- * DOCUMENT ME!
+ * Check if this timer repeatly fires.
*
- * @param lt DOCUMENT ME!
+ * @return true if this timer repeats
+ * @see #setRepeats
*/
- public static void setLogTimers(boolean lt)
+ public boolean isRepeats()
{
- logTimers = lt;
+ return repeat;
}
/**
- * DOCUMENT ME!
+ * Check if this timer coalesce events
*
- * @return DOCUMENT ME!
+ * @return true if this timer coalesce events
+ * @see #setCoalesce
*/
- public static boolean getLogTimers()
+ public boolean isCoalesce()
{
- return logTimers;
+ return coalesce;
}
/**
- * DOCUMENT ME!
+ * 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 d DOCUMENT ME!
+ * @param flag true if this timer should coalesce events
+ * @see #fireActionPerformed
*/
- public void setDelay(int d)
+ public void setCoalesce(boolean flag)
{
- delay = d;
+ coalesce = flag;
}
/**
- * DOCUMENT ME!
+ * If enabled, log some output to the stdout whenever this timer ticks.
+ * By default, false.
*
- * @return DOCUMENT ME!
+ * @param flag true if this timer should log whenever it ticks
*/
- public int getDelay()
+ public static void setLogTimers(boolean flag)
{
- return delay;
+ verbose = flag;
}
/**
- * DOCUMENT ME!
+ * Checks if this timer should log whenever it ticks
*
- * @param i DOCUMENT ME!
+ * @return true if this timer logs when it ticks
+ * @see #setLogTimers
*/
- public void setInitialDelay(int i)
+ public static boolean getLogTimers()
+ {
+ return verbose;
+ }
+
+ /**
+ * Sets the time that this timer should wait before firing
+ *
+ * @param delay the delay time in millisecond
+ * @see #setInitialDelay
+ */
+ public void setDelay(int delay)
{
- initialDelay = i;
+ interval = delay;
}
/**
- * DOCUMENT ME!
+ * Gets the delay time
*
- * @return DOCUMENT ME!
+ * @return the delay time
+ * @see #setDelay
*/
- public int getInitialDelay()
+ public int getDelay()
{
- return initialDelay;
+ return interval;
}
/**
- * DOCUMENT ME!
+ * Sets the time that this timer should wait before firing the first time.
+ * By default this is the same as the regular delay.
*
- * @param r DOCUMENT ME!
+ * @param initialDelay the initial delay
+ * @see #setDelay
*/
- public void setRepeats(boolean r)
+ public void setInitialDelay(int initialDelay)
{
- repeats = r;
+ init_delay = initialDelay;
}
/**
- * DOCUMENT ME!
+ * Sets if this timer should repeat. By default, true.
*
- * @return DOCUMENT ME!
+ * @param flag true if this timer should repeatly fires
*/
- public boolean isRepeats()
+ public void setRepeats(boolean flag)
{
- return repeats;
+ repeat = flag;
}
/**
- * DOCUMENT ME!
+ * Checks if this timer is running
*
- * @return DOCUMENT ME!
+ * @return true if this timer is running
+ * @see #start
*/
public boolean isRunning()
{
@@ -369,35 +286,162 @@
}
/**
- * DOCUMENT ME!
+ * Starts this timer if it's not started already.
+ *
+ * @see #isRunning
+ * @see #stop
*/
public void start()
{
- if (isRunning())
- return;
- waker = new Waker();
- waker.start();
+ synchronized(Timer.class)
+ {
+ if (!isRunning())
+ {
+ if(init_delay == -1)
+ init_delay = interval;
+
+ ticks = 0;
+ running = true;
+
+ putPendingTimer(this);
+ }
+ }
}
/**
- * DOCUMENT ME!
+ * Gets the initial delay
+ *
+ * @return the initial delay
+ * @see #setInitialDelay
*/
- public void restart()
+ public int getInitialDelay()
{
- stop();
- start();
+ return init_delay == -1 ? interval : init_delay;
}
/**
- * DOCUMENT ME!
+ * Stops the timer if it's running.
+ *
+ * @see #isRunning
+ * @see #start
*/
public void stop()
{
- running = false;
- waker.interrupt();
- synchronized (queueLock)
+ synchronized(Timer.class)
+ {
+ if(isRunning())
+ {
+ running = false;
+ allTimers.remove(this);
+ if(allTimers.isEmpty())
+ waker = null;
+ }
+ }
+ }
+
+ 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 long nextExecTime;
+
+ private static synchronized void putPendingTimer(Timer t)
+ {
+ 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();
}
}
+
+ private static class Waker extends Thread
+ {
+ public void run()
+ {
+ while(true)
+ {
+ List removedTimers = new LinkedList();
+
+ synchronized(Timer.class)
+ {
+ if(allTimers.isEmpty())
+ break;
+
+ Iterator iter = allTimers.iterator();
+ while(iter.hasNext())
+ {
+ Timer t = (Timer)iter.next();
+
+ if(t.nextExecTime < System.currentTimeMillis())
+ {
+ 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;
+ }
+ 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;
}