Home » openjdk-7 » javax » swing » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   
   27   
   28   package javax.swing;
   29   
   30   
   31   
   32   import java.util;
   33   import java.util.concurrent.atomic.AtomicBoolean;
   34   import java.util.concurrent.locks;
   35   import java.awt;
   36   import java.awt.event;
   37   import java.io.Serializable;
   38   import java.io;
   39   import java.security.AccessControlContext;
   40   import java.security.AccessController;
   41   import java.security.PrivilegedAction;
   42   import javax.swing.event.EventListenerList;
   43   
   44   
   45   
   46   /**
   47    * Fires one or more {@code ActionEvent}s at specified
   48    * intervals. An example use is an animation object that uses a
   49    * <code>Timer</code> as the trigger for drawing its frames.
   50    *<p>
   51    * Setting up a timer
   52    * involves creating a <code>Timer</code> object,
   53    * registering one or more action listeners on it,
   54    * and starting the timer using
   55    * the <code>start</code> method.
   56    * For example,
   57    * the following code creates and starts a timer
   58    * that fires an action event once per second
   59    * (as specified by the first argument to the <code>Timer</code> constructor).
   60    * The second argument to the <code>Timer</code> constructor
   61    * specifies a listener to receive the timer's action events.
   62    *
   63    *<pre>
   64    *  int delay = 1000; //milliseconds
   65    *  ActionListener taskPerformer = new ActionListener() {
   66    *      public void actionPerformed(ActionEvent evt) {
   67    *          <em>//...Perform a task...</em>
   68    *      }
   69    *  };
   70    *  new Timer(delay, taskPerformer).start();</pre>
   71    *
   72    * <p>
   73    * {@code Timers} are constructed by specifying both a delay parameter
   74    * and an {@code ActionListener}. The delay parameter is used
   75    * to set both the initial delay and the delay between event
   76    * firing, in milliseconds. Once the timer has been started,
   77    * it waits for the initial delay before firing its
   78    * first <code>ActionEvent</code> to registered listeners.
   79    * After this first event, it continues to fire events
   80    * every time the between-event delay has elapsed, until it
   81    * is stopped.
   82    * <p>
   83    * After construction, the initial delay and the between-event
   84    * delay can be changed independently, and additional
   85    * <code>ActionListeners</code> may be added.
   86    * <p>
   87    * If you want the timer to fire only the first time and then stop,
   88    * invoke <code>setRepeats(false)</code> on the timer.
   89    * <p>
   90    * Although all <code>Timer</code>s perform their waiting
   91    * using a single, shared thread
   92    * (created by the first <code>Timer</code> object that executes),
   93    * the action event handlers for <code>Timer</code>s
   94    * execute on another thread -- the event-dispatching thread.
   95    * This means that the action handlers for <code>Timer</code>s
   96    * can safely perform operations on Swing components.
   97    * However, it also means that the handlers must execute quickly
   98    * to keep the GUI responsive.
   99    *
  100    * <p>
  101    * In v 1.3, another <code>Timer</code> class was added
  102    * to the Java platform: <code>java.util.Timer</code>.
  103    * Both it and <code>javax.swing.Timer</code>
  104    * provide the same basic functionality,
  105    * but <code>java.util.Timer</code>
  106    * is more general and has more features.
  107    * The <code>javax.swing.Timer</code> has two features
  108    * that can make it a little easier to use with GUIs.
  109    * First, its event handling metaphor is familiar to GUI programmers
  110    * and can make dealing with the event-dispatching thread
  111    * a bit simpler.
  112    * Second, its
  113    * automatic thread sharing means that you don't have to
  114    * take special steps to avoid spawning
  115    * too many threads.
  116    * Instead, your timer uses the same thread
  117    * used to make cursors blink,
  118    * tool tips appear,
  119    * and so on.
  120    *
  121    * <p>
  122    * You can find further documentation
  123    * and several examples of using timers by visiting
  124    * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/timer.html"
  125    * target = "_top">How to Use Timers</a>,
  126    * a section in <em>The Java Tutorial.</em>
  127    * For more examples and help in choosing between
  128    * this <code>Timer</code> class and
  129    * <code>java.util.Timer</code>,
  130    * see
  131    * <a href="http://java.sun.com/products/jfc/tsc/articles/timer/"
  132    * target="_top">Using Timers in Swing Applications</a>,
  133    * an article in <em>The Swing Connection.</em>
  134    * <p>
  135    * <strong>Warning:</strong>
  136    * Serialized objects of this class will not be compatible with
  137    * future Swing releases. The current serialization support is
  138    * appropriate for short term storage or RMI between applications running
  139    * the same version of Swing.  As of 1.4, support for long term storage
  140    * of all JavaBeans<sup><font size="-2">TM</font></sup>
  141    * has been added to the <code>java.beans</code> package.
  142    * Please see {@link java.beans.XMLEncoder}.
  143    *
  144    * @see java.util.Timer <code>java.util.Timer</code>
  145    *
  146    *
  147    * @author Dave Moore
  148    */
  149   public class Timer implements Serializable
  150   {
  151       /*
  152        * NOTE: all fields need to be handled in readResolve
  153        */
  154   
  155       protected EventListenerList listenerList = new EventListenerList();
  156   
  157       // The following field strives to maintain the following:
  158       //    If coalesce is true, only allow one Runnable to be queued on the
  159       //    EventQueue and be pending (ie in the process of notifying the
  160       //    ActionListener). If we didn't do this it would allow for a
  161       //    situation where the app is taking too long to process the
  162       //    actionPerformed, and thus we'ld end up queing a bunch of Runnables
  163       //    and the app would never return: not good. This of course implies
  164       //    you can get dropped events, but such is life.
  165       // notify is used to indicate if the ActionListener can be notified, when
  166       // the Runnable is processed if this is true it will notify the listeners.
  167       // notify is set to true when the Timer fires and the Runnable is queued.
  168       // It will be set to false after notifying the listeners (if coalesce is
  169       // true) or if the developer invokes stop.
  170       private transient final AtomicBoolean notify = new AtomicBoolean(false);
  171   
  172       private volatile int     initialDelay, delay;
  173       private volatile boolean repeats = true, coalesce = true;
  174   
  175       private transient final Runnable doPostEvent;
  176   
  177       private static volatile boolean logTimers;
  178   
  179       private transient final Lock lock = new ReentrantLock();
  180   
  181       // This field is maintained by TimerQueue.
  182       // eventQueued can also be reset by the TimerQueue, but will only ever
  183       // happen in applet case when TimerQueues thread is destroyed.
  184       // access to this field is synchronized on getLock() lock.
  185       transient TimerQueue.DelayedTimer delayedTimer = null;
  186   
  187       private volatile String actionCommand;
  188   
  189       /**
  190        * Creates a {@code Timer} and initializes both the initial delay and
  191        * between-event delay to {@code delay} milliseconds. If {@code delay}
  192        * is less than or equal to zero, the timer fires as soon as it
  193        * is started. If <code>listener</code> is not <code>null</code>,
  194        * it's registered as an action listener on the timer.
  195        *
  196        * @param delay milliseconds for the initial and between-event delay
  197        * @param listener  an initial listener; can be <code>null</code>
  198        *
  199        * @see #addActionListener
  200        * @see #setInitialDelay
  201        * @see #setRepeats
  202        */
  203       public Timer(int delay, ActionListener listener) {
  204           super();
  205           this.delay = delay;
  206           this.initialDelay = delay;
  207   
  208           doPostEvent = new DoPostEvent();
  209   
  210           if (listener != null) {
  211               addActionListener(listener);
  212           }
  213       }
  214   
  215       /*
  216        * The timer's AccessControlContext.
  217        */
  218        private transient volatile AccessControlContext acc =
  219               AccessController.getContext();
  220   
  221       /**
  222         * Returns the acc this timer was constructed with.
  223         */
  224        final AccessControlContext getAccessControlContext() {
  225          if (acc == null) {
  226              throw new SecurityException(
  227                      "Timer is missing AccessControlContext");
  228          }
  229          return acc;
  230        }
  231   
  232       /**
  233        * DoPostEvent is a runnable class that fires actionEvents to
  234        * the listeners on the EventDispatchThread, via invokeLater.
  235        * @see Timer#post
  236        */
  237       class DoPostEvent implements Runnable
  238       {
  239           public void run() {
  240               if (logTimers) {
  241                   System.out.println("Timer ringing: " + Timer.this);
  242               }
  243               if(notify.get()) {
  244                   fireActionPerformed(new ActionEvent(Timer.this, 0, getActionCommand(),
  245                                                       System.currentTimeMillis(),
  246                                                       0));
  247                   if (coalesce) {
  248                       cancelEvent();
  249                   }
  250               }
  251           }
  252   
  253           Timer getTimer() {
  254               return Timer.this;
  255           }
  256       }
  257   
  258       /**
  259        * Adds an action listener to the <code>Timer</code>.
  260        *
  261        * @param listener the listener to add
  262        *
  263        * @see #Timer
  264        */
  265       public void addActionListener(ActionListener listener) {
  266           listenerList.add(ActionListener.class, listener);
  267       }
  268   
  269   
  270       /**
  271        * Removes the specified action listener from the <code>Timer</code>.
  272        *
  273        * @param listener the listener to remove
  274        */
  275       public void removeActionListener(ActionListener listener) {
  276           listenerList.remove(ActionListener.class, listener);
  277       }
  278   
  279   
  280       /**
  281        * Returns an array of all the action listeners registered
  282        * on this timer.
  283        *
  284        * @return all of the timer's <code>ActionListener</code>s or an empty
  285        *         array if no action listeners are currently registered
  286        *
  287        * @see #addActionListener
  288        * @see #removeActionListener
  289        *
  290        * @since 1.4
  291        */
  292       public ActionListener[] getActionListeners() {
  293           return listenerList.getListeners(ActionListener.class);
  294       }
  295   
  296   
  297       /**
  298        * Notifies all listeners that have registered interest for
  299        * notification on this event type.
  300        *
  301        * @param e the action event to fire
  302        * @see EventListenerList
  303        */
  304       protected void fireActionPerformed(ActionEvent e) {
  305           // Guaranteed to return a non-null array
  306           Object[] listeners = listenerList.getListenerList();
  307   
  308           // Process the listeners last to first, notifying
  309           // those that are interested in this event
  310           for (int i=listeners.length-2; i>=0; i-=2) {
  311               if (listeners[i]==ActionListener.class) {
  312                   ((ActionListener)listeners[i+1]).actionPerformed(e);
  313               }
  314           }
  315       }
  316   
  317       /**
  318        * Returns an array of all the objects currently registered as
  319        * <code><em>Foo</em>Listener</code>s
  320        * upon this <code>Timer</code>.
  321        * <code><em>Foo</em>Listener</code>s
  322        * are registered using the <code>add<em>Foo</em>Listener</code> method.
  323        * <p>
  324        * You can specify the <code>listenerType</code> argument
  325        * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
  326        * For example, you can query a <code>Timer</code>
  327        * instance <code>t</code>
  328        * for its action listeners
  329        * with the following code:
  330        *
  331        * <pre>ActionListener[] als = (ActionListener[])(t.getListeners(ActionListener.class));</pre>
  332        *
  333        * If no such listeners exist,
  334        * this method returns an empty array.
  335        *
  336        * @param listenerType  the type of listeners requested;
  337        *          this parameter should specify an interface
  338        *          that descends from <code>java.util.EventListener</code>
  339        * @return an array of all objects registered as
  340        *          <code><em>Foo</em>Listener</code>s
  341        *          on this timer,
  342        *          or an empty array if no such
  343        *          listeners have been added
  344        * @exception ClassCastException if <code>listenerType</code> doesn't
  345        *          specify a class or interface that implements
  346        *          <code>java.util.EventListener</code>
  347        *
  348        * @see #getActionListeners
  349        * @see #addActionListener
  350        * @see #removeActionListener
  351        *
  352        * @since 1.3
  353        */
  354       public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
  355           return listenerList.getListeners(listenerType);
  356       }
  357   
  358       /**
  359        * Returns the timer queue.
  360        */
  361       private TimerQueue timerQueue() {
  362           return TimerQueue.sharedInstance();
  363       }
  364   
  365   
  366       /**
  367        * Enables or disables the timer log. When enabled, a message
  368        * is posted to <code>System.out</code> whenever the timer goes off.
  369        *
  370        * @param flag  <code>true</code> to enable logging
  371        * @see #getLogTimers
  372        */
  373       public static void setLogTimers(boolean flag) {
  374           logTimers = flag;
  375       }
  376   
  377   
  378       /**
  379        * Returns <code>true</code> if logging is enabled.
  380        *
  381        * @return <code>true</code> if logging is enabled; otherwise, false
  382        * @see #setLogTimers
  383        */
  384       public static boolean getLogTimers() {
  385           return logTimers;
  386       }
  387   
  388   
  389       /**
  390        * Sets the <code>Timer</code>'s between-event delay, the number of milliseconds
  391        * between successive action events. This does not affect the initial delay
  392        * property, which can be set by the {@code setInitialDelay} method.
  393        *
  394        * @param delay the delay in milliseconds
  395        * @see #setInitialDelay
  396        */
  397       public void setDelay(int delay) {
  398           if (delay < 0) {
  399               throw new IllegalArgumentException("Invalid delay: " + delay);
  400           }
  401           else {
  402               this.delay = delay;
  403           }
  404       }
  405   
  406   
  407       /**
  408        * Returns the delay, in milliseconds,
  409        * between firings of action events.
  410        *
  411        * @see #setDelay
  412        * @see #getInitialDelay
  413        */
  414       public int getDelay() {
  415           return delay;
  416       }
  417   
  418   
  419       /**
  420        * Sets the <code>Timer</code>'s initial delay, the time
  421        * in milliseconds to wait after the timer is started
  422        * before firing the first event. Upon construction, this
  423        * is set to be the same as the between-event delay,
  424        * but then its value is independent and remains unaffected
  425        * by changes to the between-event delay.
  426        *
  427        * @param initialDelay the initial delay, in milliseconds
  428        * @see #setDelay
  429        */
  430       public void setInitialDelay(int initialDelay) {
  431           if (initialDelay < 0) {
  432               throw new IllegalArgumentException("Invalid initial delay: " +
  433                                                  initialDelay);
  434           }
  435           else {
  436               this.initialDelay = initialDelay;
  437           }
  438       }
  439   
  440   
  441       /**
  442        * Returns the <code>Timer</code>'s initial delay.
  443        *
  444        * @see #setInitialDelay
  445        * @see #setDelay
  446        */
  447       public int getInitialDelay() {
  448           return initialDelay;
  449       }
  450   
  451   
  452       /**
  453        * If <code>flag</code> is <code>false</code>,
  454        * instructs the <code>Timer</code> to send only one
  455        * action event to its listeners.
  456        *
  457        * @param flag specify <code>false</code> to make the timer
  458        *             stop after sending its first action event
  459        */
  460       public void setRepeats(boolean flag) {
  461           repeats = flag;
  462       }
  463   
  464   
  465       /**
  466        * Returns <code>true</code> (the default)
  467        * if the <code>Timer</code> will send
  468        * an action event
  469        * to its listeners multiple times.
  470        *
  471        * @see #setRepeats
  472        */
  473       public boolean isRepeats() {
  474           return repeats;
  475       }
  476   
  477   
  478       /**
  479        * Sets whether the <code>Timer</code> coalesces multiple pending
  480        * <code>ActionEvent</code> firings.
  481        * A busy application may not be able
  482        * to keep up with a <code>Timer</code>'s event generation,
  483        * causing multiple
  484        * action events to be queued.  When processed,
  485        * the application sends these events one after the other, causing the
  486        * <code>Timer</code>'s listeners to receive a sequence of
  487        * events with no delay between them. Coalescing avoids this situation
  488        * by reducing multiple pending events to a single event.
  489        * <code>Timer</code>s
  490        * coalesce events by default.
  491        *
  492        * @param flag specify <code>false</code> to turn off coalescing
  493        */
  494       public void setCoalesce(boolean flag) {
  495           boolean old = coalesce;
  496           coalesce = flag;
  497           if (!old && coalesce) {
  498               // We must do this as otherwise if the Timer once notified
  499               // in !coalese mode notify will be stuck to true and never
  500               // become false.
  501               cancelEvent();
  502           }
  503       }
  504   
  505   
  506       /**
  507        * Returns <code>true</code> if the <code>Timer</code> coalesces
  508        * multiple pending action events.
  509        *
  510        * @see #setCoalesce
  511        */
  512       public boolean isCoalesce() {
  513           return coalesce;
  514       }
  515   
  516   
  517       /**
  518        * Sets the string that will be delivered as the action command
  519        * in <code>ActionEvent</code>s fired by this timer.
  520        * <code>null</code> is an acceptable value.
  521        *
  522        * @param command the action command
  523        * @since 1.6
  524        */
  525       public void setActionCommand(String command) {
  526           this.actionCommand = command;
  527       }
  528   
  529   
  530       /**
  531        * Returns the string that will be delivered as the action command
  532        * in <code>ActionEvent</code>s fired by this timer. May be
  533        * <code>null</code>, which is also the default.
  534        *
  535        * @return the action command used in firing events
  536        * @since 1.6
  537        */
  538       public String getActionCommand() {
  539           return actionCommand;
  540       }
  541   
  542   
  543       /**
  544        * Starts the <code>Timer</code>,
  545        * causing it to start sending action events
  546        * to its listeners.
  547        *
  548        * @see #stop
  549        */
  550        public void start() {
  551           timerQueue().addTimer(this, getInitialDelay());
  552       }
  553   
  554   
  555       /**
  556        * Returns <code>true</code> if the <code>Timer</code> is running.
  557        *
  558        * @see #start
  559        */
  560       public boolean isRunning() {
  561           return timerQueue().containsTimer(this);
  562       }
  563   
  564   
  565       /**
  566        * Stops the <code>Timer</code>,
  567        * causing it to stop sending action events
  568        * to its listeners.
  569        *
  570        * @see #start
  571        */
  572       public void stop() {
  573           getLock().lock();
  574           try {
  575               cancelEvent();
  576               timerQueue().removeTimer(this);
  577           } finally {
  578               getLock().unlock();
  579           }
  580       }
  581   
  582   
  583       /**
  584        * Restarts the <code>Timer</code>,
  585        * canceling any pending firings and causing
  586        * it to fire with its initial delay.
  587        */
  588       public void restart() {
  589           getLock().lock();
  590           try {
  591               stop();
  592               start();
  593           } finally {
  594               getLock().unlock();
  595           }
  596       }
  597   
  598   
  599       /**
  600        * Resets the internal state to indicate this Timer shouldn't notify
  601        * any of its listeners. This does not stop a repeatable Timer from
  602        * firing again, use <code>stop</code> for that.
  603        */
  604       void cancelEvent() {
  605           notify.set(false);
  606       }
  607   
  608   
  609       void post() {
  610            if (notify.compareAndSet(false, true) || !coalesce) {
  611                AccessController.doPrivileged(new PrivilegedAction<Void>() {
  612                    public Void run() {
  613                        SwingUtilities.invokeLater(doPostEvent);
  614                        return null;
  615                   }
  616               }, getAccessControlContext());
  617           }
  618       }
  619   
  620       Lock getLock() {
  621           return lock;
  622       }
  623   
  624       private void readObject(ObjectInputStream in)
  625           throws ClassNotFoundException, IOException
  626       {
  627           this.acc = AccessController.getContext();
  628           in.defaultReadObject();
  629       }
  630   
  631       /*
  632        * We have to use readResolve because we can not initialize final
  633        * fields for deserialized object otherwise
  634        */
  635       private Object readResolve() {
  636           Timer timer = new Timer(getDelay(), null);
  637           timer.listenerList = listenerList;
  638           timer.initialDelay = initialDelay;
  639           timer.delay = delay;
  640           timer.repeats = repeats;
  641           timer.coalesce = coalesce;
  642           timer.actionCommand = actionCommand;
  643           return timer;
  644       }
  645   }

Home » openjdk-7 » javax » swing » [javadoc | source]