All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.assertj.swing.edt.GuiActionRunner Maven / Gradle / Ivy

There is a newer version: 3.17.1
Show newest version
/**
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * Copyright 2012-2015 the original author or authors.
 */
package org.assertj.swing.edt;

import static javax.swing.SwingUtilities.invokeLater;
import static javax.swing.SwingUtilities.isEventDispatchThread;
import static org.assertj.core.util.Throwables.appendStackTraceInCurrentThreadToThrowable;
import static org.assertj.swing.exception.UnexpectedException.unexpected;

import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;

/**
 * Executes instances of {@link GuiQuery} and {@link GuiTask}.
 *
 * @author Alex Ruiz
 */
@ThreadSafe
public class GuiActionRunner {
  @GuardedBy("this")
  private static boolean executeInEDT = true;

  /**
   * Indicates {@link GuiActionRunner} whether instances of {@link GuiQuery} and {@link GuiTask} should be executed in
   * the event dispatch thread (EDT).
   *
   * @param b if {@code true}, GUI actions are executed in the event dispatch thread (EDT). If {@code false}, GUI
   *          actions are executed in the current thread.
   */
  public static synchronized void executeInEDT(boolean b) {
    executeInEDT = b;
  }

  /**
   * Indicates whether instances of {@link GuiQuery} and {@link GuiTask} should be executed in the event dispatch thread
   * (EDT).
   *
   * @return {@code true} if GUI actions are executed in the event dispatch thread, {@code false} otherwise.
   */
  public static synchronized boolean executeInEDT() {
    return executeInEDT;
  }

  /**
   * Executes the given callable in the event dispatch thread (EDT). This method waits until the query has finished its
   * execution.
   *
   * @param query the query to execute.
   * @param  the return type of the action to execute.
   * @return the result of the query executed in the main thread.
   * @throws org.assertj.swing.exception.UnexpectedException wrapping any checked exception thrown when executing
   *           the given query in the event dispatch thread (EDT). Unchecked exceptions are re-thrown without any
   *           wrapping.
   * @see #execute(GuiQuery)
   */
  public static @Nullable  T execute(@Nonnull Callable query) {
    return execute(new GuiQuery() {
      @Override
      protected T executeInEDT() throws Throwable {
        return query.call();
      }
    });
  }

  /**
   * Executes the given query in the event dispatch thread (EDT). This method waits until the query has finished its
   * execution.
   *
   * @param query the query to execute.
   * @param  the return type of the action to execute.
   * @return the result of the query executed in the main thread.
   * @throws org.assertj.swing.exception.UnexpectedException wrapping any checked exception thrown when executing
   *           the given query in the
   *           event dispatch thread (EDT). Unchecked exceptions are re-thrown without any wrapping.
   * @see #executeInEDT()
   * @see #execute(Callable)
   */
  public static @Nullable  T execute(@Nonnull GuiQuery query) {
    if (!executeInEDT) {
      return executeInCurrentThread(query);
    }
    run(query);
    return resultOf(query);
  }

  private static @Nullable  T executeInCurrentThread(@Nonnull GuiQuery query) {
    try {
      return query.executeInEDT();
    } catch (Throwable e) {
      throw unexpected(e);
    }
  }

  /**
   * Executes the given runnable in the event dispatch thread (EDT). This method waits until the task has finished its
   * execution.
   *
   * @param task the task to execute.
   * @throws org.assertj.swing.exception.UnexpectedException wrapping any checked exception thrown when executing
   *           the given query in the event dispatch thread (EDT). Unchecked exceptions are re-thrown without any
   *           wrapping.
   * @see #executeInEDT()
   * @see #execute(GuiTask)
   */
  public static void execute(@Nonnull GuiActionRunnable task) {
    execute(new GuiTask() {
      @Override
      protected void executeInEDT() throws Throwable {
        task.run();
      }
    });
  }

  /**
   * Executes the given task in the event dispatch thread (EDT). This method waits until the task has finished its
   * execution.
   *
   * @param task the task to execute.
   * @throws org.assertj.swing.exception.UnexpectedException wrapping any checked exception thrown when executing
   *           the given query in the
   *           event dispatch thread (EDT). Unchecked exceptions are re-thrown without any wrapping.
   * @see #executeInEDT()
   * @see #execute(Runnable)
   */
  public static void execute(@Nonnull GuiTask task) {
    if (!executeInEDT) {
      executeInCurrentThread(task);
      return;
    }
    run(task);
    rethrowCaughtExceptionIn(task);
  }

  private static void executeInCurrentThread(@Nonnull GuiTask task) {
    try {
      task.executeInEDT();
    } catch (Throwable e) {
      throw unexpected(e);
    }
  }

  private static void run(@Nonnull final GuiAction action) {
    if (isEventDispatchThread()) {
      action.run();
      return;
    }
    final CountDownLatch latch = new CountDownLatch(1);
    action.executionNotification(latch);
    invokeLater(action);
    try {
      latch.await();
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
  }

  private static @Nullable  T resultOf(@Nonnull GuiQuery query) {
    T result = query.result();
    query.clearResult();
    rethrowCaughtExceptionIn(query);
    return result;
  }

  /**
   * Wraps, with a {@link UnexpectedException}, and re-throws any caught exception in the given action.
   *
   * @param action the given action that may have a caught exception during its execution.
   * @throws UnexpectedException wrapping any checked exception thrown when executing the given query in the
   *           event dispatch thread (EDT). Unchecked exceptions are re-thrown without any wrapping.
   */
  private static void rethrowCaughtExceptionIn(@Nonnull GuiAction action) {
    Throwable caughtException = action.catchedException();
    action.clearCaughtException();
    if (caughtException == null) {
      return;
    }
    if (caughtException instanceof RuntimeException) {
      appendStackTraceInCurrentThreadToThrowable(caughtException, "execute");
      throw (RuntimeException) caughtException;
    }
    if (caughtException instanceof Error) {
      caughtException.fillInStackTrace();
      throw (Error) caughtException;
    }
    throw unexpected(caughtException);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy