On Wed, Jun 30, 2004 at 08:34:08AM +0200, Roman Kennke wrote:
> Well, actually it did. Yesterday I tried to merge your JSpinner
> implementation into my classpath tree, but it failed with the timer
> patch. Maybe you have to look into this again, as Timer has moved on in
> the meantime.

Okay this is partly my fault for failed to get my FSF paperwork ready early,
which resulted in people breaking it. Attached is a updated patch, totally
untested (may not even compile, but should apply).

Anyone else using jhbuild'ed gcj? Any suggestion on how to maintain parallel
trees? Like in this case I cannot test the updated patch since I have other
not-yet ready changes, it would be nice if there's an easy way (that does not
involve rebuilding all of gcj on another prefix) that would be great.

Oh just to make sure that we aren't duplicating effort, I've been working on
tooltip stuff. When I started Thomas told me that no one was working on it.
Also it would be really great if people would leave Timer.java alone for a
while :-)

-khc
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 <code>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 <code>ActionListener</code> to the list of listeners to invoke
+   *
+   * @param listener the <code>ActionListener</code> to add
+   * @see java.awt.event.ActionListener
+   */
   public void addActionListener(ActionListener listener)
   {
-    listenerList.add (ActionListener.class, listener);
+    listenerList.add(ActionListener.class, listener);
   }
-  
+
+  /**
+   * Removes an <code>ActionListener</code>
+   *
+   * @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 <code>ActionListener</code>
+   * @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
+   * <code>ActionListener</code> may receive multiple 
+   * <code>actionPerformed</code> 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;
 }
_______________________________________________
Classpath mailing list
[EMAIL PROTECTED]
http://lists.gnu.org/mailman/listinfo/classpath

Reply via email to