package com.laytonsmith.abstraction; import com.laytonsmith.PureUtilities.DaemonManager; import com.laytonsmith.core.events.BindableEvent; import com.laytonsmith.core.events.Driver; import com.laytonsmith.core.events.EventUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicInteger; /** * * */ public abstract class AbstractConvertor implements Convertor{ private final List<Runnable> shutdownHooks = new ArrayList<>(); @Override public void addShutdownHook(Runnable r) { shutdownHooks.add(r); } @Override public void runShutdownHooks() { // Fire off the shutdown event, before we shut down all the internal hooks EventUtils.TriggerListener(Driver.SHUTDOWN, "shutdown", new BindableEvent() { @Override public Object _GetObject() { return new Object(); } }); Iterator<Runnable> iter = shutdownHooks.iterator(); while(iter.hasNext()){ iter.next().run(); iter.remove(); } } /** * Runs the task either now or later. In the case of a default Convertor, * it just runs the task now. * @param dm * @param r */ @Override public void runOnMainThreadLater(DaemonManager dm, Runnable r) { r.run(); } @Override public <T> T runOnMainThreadAndWait(Callable<T> callable) throws Exception{ return (T) callable.call(); } @Override public MCWorldCreator getWorldCreator(String worldName) { throw new UnsupportedOperationException("Not supported."); } @Override public MCCommand getNewCommand(String name) { throw new UnsupportedOperationException("Not supported in this implementation."); } @Override public MCCommandSender GetCorrectSender(MCCommandSender unspecific) { throw new UnsupportedOperationException("Not supported in this implementation."); } private final Map<Integer, Task> tasks = new HashMap<>(); private final AtomicInteger taskIDs = new AtomicInteger(0); @Override public void ClearAllRunnables() { synchronized(tasks){ for(Task task : tasks.values()){ task.unregister(); } tasks.clear(); } } @Override public void ClearFutureRunnable(int id) { synchronized(tasks){ if(tasks.containsKey(id)){ tasks.get(id).unregister(); tasks.remove(id); } } } @Override public int SetFutureRepeater(DaemonManager dm, long ms, long initialDelay, final Runnable r) { int id = taskIDs.getAndIncrement(); Task t = new Task(id, dm, true, initialDelay, ms, new Runnable() { @Override public void run() { triggerRunnable(r); } }); synchronized(tasks){ tasks.put(id, t); t.register(); } return id; } @Override public int SetFutureRunnable(DaemonManager dm, long ms, final Runnable r) { int id = taskIDs.getAndIncrement(); Task t = new Task(id, dm, false, ms, 0, new Runnable() { @Override public void run() { triggerRunnable(r); } }); synchronized(tasks){ tasks.put(id, t); t.register(); } return id; } /** * A subclass may need to do special handling for the actual trigger of a scheduled * task, though not need to do anything special for the scheduling itself. In this * case, subclasses may override this method, and whenever a scheduled task is * intended to be run, it will be passed to this method instead. By default, the * runnable is simply run. * @param r */ protected synchronized void triggerRunnable(Runnable r){ r.run(); } private class Task { /** * The task id */ private final int id; /** * The DaemonManager */ private final DaemonManager dm; /** * True if this is an interval, false otherwise. */ private final boolean repeater; /** * The initial delay. For timeouts, this is just the delay. */ private final long initialDelay; /** * The delay between triggers. For intervals, this is ignored. */ private final long interval; /** * The task itself. */ private final Runnable task; private Timer timer; public Task(int id, DaemonManager dm, boolean repeater, long initialDelay, long interval, Runnable task){ this.id = id; this.dm = dm; this.repeater = repeater; this.initialDelay = initialDelay; if(repeater){ this.interval = interval; } else { this.interval = Long.MAX_VALUE; } this.task = task; } public void register(){ timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { task.run(); if(!repeater){ unregister(); synchronized(tasks){ tasks.remove(id); } } } }, initialDelay, interval); if(dm != null){ dm.activateThread(null); } } public void unregister(){ timer.cancel(); if(dm != null){ dm.deactivateThread(null); } } public int getId(){ return id; } } }