package at.favre.tools.dconvert;
import at.favre.tools.dconvert.arg.Arguments;
import at.favre.tools.dconvert.converters.IPlatformConverter;
import at.favre.tools.dconvert.converters.Result;
import at.favre.tools.dconvert.converters.postprocessing.IPostProcessor;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Handles post processing tasks
*/
public class WorkerHandler<T> {
private final List<T> processors;
private final ExecutorService threadPool;
private final Arguments arguments;
private final Callback callback;
private int jobCount;
public WorkerHandler(List<T> processors, Arguments arguments, Callback callback) {
this.processors = processors;
this.threadPool = new ThreadPoolExecutor(arguments.threadCount, arguments.threadCount, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1024 * 10));
this.callback = callback;
this.arguments = arguments;
}
public void start(List<File> allFiles) {
this.jobCount = allFiles.size() * processors.size();
InternalCallback internalCallback = new InternalCallback(callback);
for (T processor : processors) {
for (File fileToProcess : allFiles) {
threadPool.execute(new Worker(fileToProcess, processor, arguments, internalCallback));
}
}
threadPool.shutdown();
if (jobCount == 0) {
callback.onFinished(0, Collections.emptyList(), new StringBuilder(), Collections.emptyList(), false);
}
}
private class Worker implements Runnable {
private File unprocessedFile;
private T processor;
private InternalCallback callback;
private final Arguments arguments;
public Worker(File unprocessedFile, T processors, Arguments arguments, InternalCallback callback) {
this.unprocessedFile = unprocessedFile;
this.arguments = arguments;
this.processor = processors;
this.callback = callback;
}
@Override
public void run() {
Result result = null;
if (IPostProcessor.class.isInstance(processor)) {
result = ((IPostProcessor) processor).process(unprocessedFile, arguments.keepUnoptimizedFilesPostProcessor);
} else if (IPlatformConverter.class.isInstance(processor)) {
result = ((IPlatformConverter) processor).convert(unprocessedFile, arguments);
}
callback.onJobFinished(result);
}
}
private class InternalCallback {
private int currentJobCount = 0;
private List<Exception> exceptionList = new ArrayList<>();
private Callback callback;
private StringBuilder logBuilder = new StringBuilder();
private boolean canceled = false;
private List<File> files = new ArrayList<>();
public InternalCallback(Callback callback) {
this.callback = callback;
}
synchronized void onJobFinished(Result result) {
if (!canceled) {
currentJobCount++;
if (result != null) {
if (result.log != null && result.log.length() > 0) {
logBuilder.append(result.log).append("\n");
}
if (result.processedFiles != null) {
files.addAll(result.processedFiles);
}
if (result.exception != null) {
exceptionList.add(result.exception);
if (arguments.haltOnError) {
canceled = true;
threadPool.shutdownNow();
callback.onFinished(currentJobCount, files, logBuilder, exceptionList, true);
}
}
}
if (!canceled) {
if (currentJobCount == jobCount) {
callback.onFinished(currentJobCount, files, logBuilder, exceptionList, false);
} else {
callback.onProgress((float) currentJobCount / (float) jobCount);
}
}
}
}
}
public interface Callback {
void onProgress(float percent);
void onFinished(int finishedJobs, List<File> outFiles, StringBuilder log, List<Exception> exceptions, boolean haltedDuringProcess);
}
}