package sander;
import java.util.ArrayList;
import static java.util.Collections.addAll;
import java.util.List;
import static org.bukkit.Bukkit.getScheduler;
import org.bukkit.plugin.Plugin;
public class MainThreadPlanner<T> {
private AccumulativeRunnable<T> doProcess;
private final AccumulativeRunnable<Runnable> doSubmit;
protected final Plugin plugin;
private final Processor<T> processor;
public MainThreadPlanner(Plugin plugin,Processor<T> processor) {
this.doSubmit = new DoSubmitAccumulativeRunnable(plugin);
this.plugin = plugin;
this.processor = processor;
}
private void process(List<T> chunks) {
this.processor.process(chunks);
}
/**
* Sends data chunks to the {@link #process} method. This method is to be
* used from inside the {@code doInBackground} method to deliver
* intermediate results for processing on the <i>Bukkit Main Server
* Thread</i> inside the {@code process} method.
*
* <p>
* Because the {@code process} method is invoked asynchronously on the
* <i>Bukkit Main Server Thread</i>
* multiple invocations to the {@code publish} method might occur before the
* {@code process} method is executed. For performance purposes all these
* invocations are coalesced into one invocation with concatenated
* arguments.
*
* <p>
* For example:
*
* <pre>
* publish("1");
* publish("2", "3");
* publish("4", "5", "6");
* </pre>
*
* might result in:
*
* <pre>
* process("1", "2", "3", "4", "5", "6")
* </pre>
*
*
* @param chunks intermediate results to process
*
* @see #process
*
*/
public final void publish(T... chunks) {
synchronized (this) {
if (this.doProcess == null) {
this.doProcess = new AccumulativeRunnable<T>() {
@Override
public void run(List<T> args) {
MainThreadPlanner.this.process(args);
}
@Override
protected void submit() {
MainThreadPlanner.this.doSubmit.add(this);
}
};
}
}
this.doProcess.add(chunks);
}
private static abstract class AccumulativeRunnable<T extends Object> implements Runnable {
private List<T> arguments = null;
protected abstract void run(List<T> paramList);
@Override
public final void run() {
this.run(this.flush());
}
public final synchronized void add(T... toAdd) {
boolean mustSubmit = false;
if (this.arguments == null) {
mustSubmit = true;
this.arguments = new ArrayList<>();
}
addAll(this.arguments, toAdd);
if (mustSubmit) {
this.submit();
}
}
abstract protected void submit();
private synchronized List<T> flush() {
List<T> localList = this.arguments;
this.arguments = null;
return localList;
}
}
public interface Processor<T> {
public void process(List<T> process);
}
private class DoSubmitAccumulativeRunnable extends AccumulativeRunnable<Runnable> implements Runnable {
/**
* Time in ticks between 2 invokings of the schedular
*/
private final static int DELAY = 1;
/**
* The plugin, used to schedule tasks
*/
private final Plugin plugin;
public DoSubmitAccumulativeRunnable(Plugin plugin) {
this.plugin = plugin;
}
@Override
protected void run(List<Runnable> args) {
for (Runnable runnable : args) {
runnable.run();
}
}
@Override
protected void submit() {
if (this.plugin.isEnabled()) {
getScheduler().scheduleSyncDelayedTask(this.plugin, this, DELAY);
} else {
// What do do, scheduling an task would throw an IllegalArgumentException, and enablking this plugin for a little time may cause bugs inside the plugin?
// Mayby use reflection to access the enabled field of JavaPlugin, but this wont work whit plugins that dont exend that class
throw new IllegalStateException("Plugin not enabled, unable to schadule task");
}
}
}
}