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 }