/*
* Copyright (c) 2008 Wayne Meissner
*
* This file is part of gstreamer-java.
*
* This code is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* version 3 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gstreamer.swing;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.Callable;
import java.util.concurrent.Delayed;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.SwingUtilities;
/**
* A ScheduledExecutorService that executes tasks and timers on the AWT/Swing EDT.
*/
public class SwingExecutorService extends AbstractExecutorService implements ScheduledExecutorService {
private final AtomicBoolean isShutDown = new AtomicBoolean(false);
/**
* Creates a new <tt>ScheduledExecutorService</tt>.
*/
public SwingExecutorService() {
}
/**
* Creates a proxy version of <tt>interfaceClass</tt> that executes <tt>instance</tt>
* on the Swing EDT when any of its methods are invoked.
*
* @param interfaceClass the interface to generate.
* @param instance the instance to delegate calls to.
* @return a new instance of <tt>interfaceclass</tt>.
*/
public <T> T wrap(Class<T> interfaceClass, T instance) {
return interfaceClass.cast(Proxy.newProxyInstance(interfaceClass.getClassLoader(),
new Class[]{ interfaceClass },
new ExecutorInvocationProxy(instance, this)));
}
/**
* Stops this executor service from accepting any more tasks.
*/
public void shutdown() {
isShutDown.set(true);
}
public List<Runnable> shutdownNow() {
return Collections.emptyList();
}
public boolean isShutdown() {
return isShutDown.get();
}
public boolean isTerminated() {
return isShutdown();
}
public boolean awaitTermination(long timeout, TimeUnit units) throws InterruptedException {
return false;
}
public void execute(Runnable task) {
SwingUtilities.invokeLater(task);
}
private static class SwingFuture<V> extends FutureTask<V>
implements ScheduledFuture<V>, ActionListener {
private final javax.swing.Timer timer;
SwingFuture(Callable<V> call, long initialDelay, long interval, TimeUnit unit) {
super(call);
timer = new javax.swing.Timer(1, this);
timer.setInitialDelay(Math.max(1, (int) unit.toMillis(initialDelay)));
// set the between-event delay
timer.setDelay(Math.max(1, (int) unit.toMillis(interval)));
// Don't coalesce events - some code will depend on the timer being
// fired every time.
timer.setCoalesce(false);
}
public long getDelay(TimeUnit unit) {
return unit.convert(timer.getDelay(), TimeUnit.MILLISECONDS);
}
public int compareTo(Delayed delayed) {
Long lhs = getDelay(TimeUnit.NANOSECONDS);
Long rhs = delayed.getDelay(TimeUnit.NANOSECONDS);
return lhs.compareTo(rhs);
}
public void actionPerformed(ActionEvent arg0) {
if (timer.getDelay() != 0) {
runAndReset();
} else {
run();
}
}
}
private <V> ScheduledFuture<V> schedule(Callable<V> callable,
long initialDelay, long interval, TimeUnit units) {
SwingFuture<V> f = new SwingFuture<V>(callable, initialDelay, interval, units);
f.timer.start();
return f;
}
public ScheduledFuture<?> schedule(Runnable task, long initialDelay, TimeUnit units) {
return schedule(Executors.callable(task), initialDelay, 0, units);
}
public <V> ScheduledFuture<V> schedule(Callable<V> task, long initialDelay,
TimeUnit units) {
return schedule(task, initialDelay, 0, units);
}
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay,
long interval, TimeUnit units) {
return schedule(Executors.callable(task), initialDelay, interval, units);
}
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay,
long interval, TimeUnit units) {
return schedule(Executors.callable(task), initialDelay, interval, units);
}
/**
* Provides a way of automagically executing methods on an interface on a
* different thread.
*/
private static class ExecutorInvocationProxy implements InvocationHandler {
private final Executor executor;
private final Object object;
public ExecutorInvocationProxy(Object object, Executor executor) {
this.object = object;
this.executor = executor;
}
public Object invoke(Object self, final Method method, final Object[] argArray) throws Throwable {
if (method.getName().equals("hashCode")) {
return object.hashCode();
} else if (method.getName().equals("equals")) {
return object.equals(argArray[0]);
}
executor.execute(new Runnable() {
public void run() {
try {
method.invoke(object, argArray);
} catch (Throwable t) {}
}
});
return null;
}
}
}