001 /*
002 * Created on Aug 14, 2008
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 *
014 * Copyright @2008-2010 the original author or authors.
015 */
016 package org.fest.swing.edt;
017
018 import static javax.swing.SwingUtilities.invokeLater;
019 import static javax.swing.SwingUtilities.isEventDispatchThread;
020 import static org.fest.util.Throwables.appendCurrentThreadStackTraceToThrowable;
021 import static org.fest.swing.exception.UnexpectedException.unexpected;
022
023 import java.util.concurrent.CountDownLatch;
024
025 import net.jcip.annotations.GuardedBy;
026 import net.jcip.annotations.ThreadSafe;
027
028 import org.fest.swing.exception.UnexpectedException;
029
030 /**
031 * Understands running instances of <code>{@link GuiQuery}</code> and <code>{@link GuiTask}</code>.
032 *
033 * @author Alex Ruiz
034 */
035 @ThreadSafe
036 public class GuiActionRunner {
037
038 @GuardedBy("this")
039 private static boolean executeInEDT = true;
040
041 /**
042 * Indicates <code>{@link GuiActionRunner}</code> if instances of <code>{@link GuiQuery}</code> and
043 * <code>{@link GuiTask}</code> should be executed in the event dispatch thread or not.
044 * @param b if <code>true</code>, GUI actions are executed in the event dispatch thread. If <code>false</code>,
045 * GUI actions are executed in the current thread.
046 */
047 public static synchronized void executeInEDT(boolean b) {
048 executeInEDT = b;
049 }
050
051 /**
052 * Returns whether instances of <code>{@link GuiQuery}</code> and <code>{@link GuiTask}</code> should be executed in
053 * the event dispatch thread or not.
054 * @return <code>true</code> if GUI actions are executed in the event dispatch thread, <code>false</code> otherwise.
055 */
056 public static synchronized boolean executeInEDT() {
057 return executeInEDT;
058 }
059
060 /**
061 * Executes the given query in the event dispatch thread. This method waits until the query has finished its
062 * execution.
063 * @param <T> the generic type of the return value.
064 * @param query the query to execute.
065 * @return the result of the query executed in the main thread.
066 * @throws UnexpectedException wrapping any <b>checked</b> exception thrown when executing the given query in the
067 * event dispatch thread. Unchecked exceptions are re-thrown without any wrapping.
068 * @see #executeInEDT()
069 */
070 public static <T> T execute(GuiQuery<T> query) {
071 if (!executeInEDT) return executeInCurrentThread(query);
072 run(query);
073 return resultOf(query);
074 }
075
076 private static <T> T executeInCurrentThread(GuiQuery<T> query) {
077 try {
078 return query.executeInEDT();
079 } catch (Throwable e) {
080 throw unexpected(e);
081 }
082 }
083
084 /**
085 * Executes the given task in the event dispatch thread. This method waits until the task has finished its execution.
086 * @param task the task to execute.
087 * @throws UnexpectedException wrapping any <b>checked</b> exception thrown when executing the given query in the
088 * event dispatch thread. Unchecked exceptions are re-thrown without any wrapping.
089 * @see #executeInEDT()
090 */
091 public static void execute(GuiTask task) {
092 if (!executeInEDT) {
093 executeInCurrentThread(task);
094 return;
095 }
096 run(task);
097 rethrowCatchedExceptionIn(task);
098 }
099
100 private static void executeInCurrentThread(GuiTask task) {
101 try {
102 task.executeInEDT();
103 } catch (Throwable e) {
104 throw unexpected(e);
105 }
106 }
107
108 private static void run(final GuiAction action) {
109 if (isEventDispatchThread()) {
110 action.run();
111 return;
112 }
113 final CountDownLatch latch = new CountDownLatch(1);
114 action.executionNotification(latch);
115 invokeLater(action);
116 try {
117 latch.await();
118 } catch (final InterruptedException e) {
119 Thread.currentThread().interrupt();
120 }
121 }
122
123 private static <T> T resultOf(GuiQuery<T> query) {
124 T result = query.result();
125 query.clearResult();
126 rethrowCatchedExceptionIn(query);
127 return result;
128 }
129
130 /**
131 * Wraps (with a <code>{@link UnexpectedException}</code>) and retrows any catched exception in the given action.
132 * @param action the given action that may have a catched exception during its execution.
133 * @throws UnexpectedException wrapping any <b>checked</b> exception thrown when executing the given query in the
134 * event dispatch thread. Unchecked exceptions are rethrown without any wrapping.
135 */
136 private static void rethrowCatchedExceptionIn(GuiAction action) {
137 Throwable catchedException = action.catchedException();
138 action.clearCatchedException();
139 if (catchedException == null) return;
140 if (catchedException instanceof RuntimeException) {
141 appendCurrentThreadStackTraceToThrowable(catchedException, "execute");
142 throw (RuntimeException)catchedException;
143 }
144 if (catchedException instanceof Error) {
145 catchedException.fillInStackTrace();
146 throw (Error)catchedException;
147 }
148 throw unexpected(catchedException);
149 }
150 }