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 }