001    /*
002     * Copyright 2006 Google Inc.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package com.google.gwt.user.client;
017    
018    import java.util.Vector;
019    
020    /**
021     * This class allows you to execute code after all currently pending event
022     * handlers have completed, using the {@link #add(Command)} method. This is
023     * useful when you need to execute code outside of the context of the current
024     * stack.
025     */
026    public class DeferredCommand {
027    
028      /**
029       * The list of commands to be processed the next time a timer event is
030       * handled.
031       */
032      private static Vector deferredCommands = new Vector();
033    
034      /**
035       * Records whether a timer is pending so we don't set multiple ones.
036       */
037      private static boolean timerIsActive = false;
038    
039      /**
040       * Enqueues a {@link Command} to be fired after all current events have been
041       * handled.
042       * 
043       * @param cmd the command to be fired. If cmd is null, a "pause" will be
044       *          inserted into the queue. Any events added after the pause will
045       *          wait for an additional cycle through the system event loop before
046       *          executing. Pauses are cumulative.
047       */
048      public static void add(Command cmd) {
049        deferredCommands.add(cmd);
050        maybeSetDeferredCommandTimer();
051      }
052    
053      /**
054       * Executes the current set of deferred commands before returning.
055       */
056      private static void flushDeferredCommands() {
057        /*
058         * Only execute the commands present at the beginning, and always pull from
059         * the beginning of the list. This ensures that if any commands are added
060         * while executing the current ones, they will stay in the list for the next
061         * pass (otherwise, they wouldn't appear deferred).
062         * 
063         * If a deferred command throws an exception, that's okay, we'll just pick
064         * up where we left off on the next tick.
065         */
066        for (int i = 0, max = deferredCommands.size(); i < max; ++i) {
067          Command current = (Command) deferredCommands.remove(0);
068          if (current == null) {
069            /*
070             * This is an indication that we should defer everything else in the
071             * list until the next tick. Leave everything else in the queue.
072             */
073            return;
074          } else {
075            current.execute();
076          }
077        }
078      }
079    
080      private static void maybeSetDeferredCommandTimer() {
081        if (!timerIsActive && !deferredCommands.isEmpty()) {
082          // There are some deferred commands in the queue.
083          // Make sure a timer will fire for them.
084          new Timer() {
085            public void run() {
086              try {
087    
088                // execute the pending commands
089                flushDeferredCommands();
090    
091              } finally {
092                // this timer has now fired and will not fire again
093                timerIsActive = false;
094    
095                // always setup the next timer, even if we're throwing an exception
096                maybeSetDeferredCommandTimer();
097              }
098            }
099          }.schedule(1);
100          timerIsActive = true;
101        }
102      }
103    }