package com.narrowtux.fmm.util; import com.google.common.io.LittleEndianDataInputStream; import com.narrowtux.fmm.gui.Controller; import com.narrowtux.fmm.io.tasks.TaskService; import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.JavaFXBuilderFactory; import javafx.scene.Node; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.text.DecimalFormat; import java.util.HashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Supplier; public class Util { private static HashMap<URL, LoadTask> preloadTasks = new HashMap<>(); private static HashMap<URL, Node> loaded = new HashMap<>(); private static ExecutorService executorService = Executors.newCachedThreadPool(); private static DecimalFormat[] decimalFormats = { new DecimalFormat("#"), // B new DecimalFormat("#.0"), // KiB new DecimalFormat("#.0"), // MiB new DecimalFormat("#.00"), // GiB new DecimalFormat("#.000"), // TiB new DecimalFormat("#.000"), // PiB new DecimalFormat("#.000"), // EiB new DecimalFormat("#.000"), // ZiB new DecimalFormat("#.000") // YiB }; private static String[] binaryIECMultipliers = { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; public static String formatBytes(long bytes) { int currentMultiplier = 0; long tmp = bytes; while (tmp >= 1024) { tmp /= 1024; currentMultiplier ++; } double res = (double) bytes / Math.pow(1024, currentMultiplier); return decimalFormats[currentMultiplier].format(res) + " " + (currentMultiplier > 0 ? binaryIECMultipliers[currentMultiplier - 1] : "") + "B"; } public static String readString(LittleEndianDataInputStream objectInputStream) throws IOException { int size = objectInputStream.readInt(); byte read[] = new byte[size]; objectInputStream.read(read); return new String(read); } public static <N extends Node, C extends Controller> LoadResult<N, C> loadFXML(URL resource, Supplier<C> controller) throws IOException { if (preloadTasks.containsKey(resource)) { try { LoadTask loadTask = preloadTasks.get(resource); if (loadTask.getControllerTask().getController() == null) { loadTask.getControllerTask().setController(controller.get()); } FXMLLoader loader = loadTask.get(); return new LoadResult<>(loader.getRoot(), loader.getController()); } catch (InterruptedException e) { } catch (Exception e) { e.printStackTrace(); } } N ret = FXMLLoader.load(resource, null, new JavaFXBuilderFactory(), clazz -> controller.get()); loaded.put(resource, ret); return new LoadResult<>(ret, controller.get()); } public static class LoadResult<N extends Node, C extends Controller> { private N node; private C controller; public LoadResult(N node, C controller) { this.node = node; this.controller = controller; } public N getNode() { return node; } public C getController() { return controller; } } public static <N extends Node, C extends Controller> LoadResult<N, C> loadFXML(String resourceUrl, Supplier<C> controller) throws IOException { return loadFXML(Util.class.getResource(resourceUrl), controller); } public static void preloadFXML(URL resource) { LoadTask task = new LoadTask(resource); preloadTasks.put(resource, task); executorService.submit(task); } public static void preloadFXML(URL resource, Controller controller) { LoadTask task = new LoadTask(resource, controller); preloadTasks.put(resource, task); executorService.submit(task); } public static void preloadFXML(String resourceUrl, Controller controller) { preloadFXML(Util.class.getResource(resourceUrl), controller); } public static void preloadFXML(String resourceUrl) { preloadFXML(Util.class.getResource(resourceUrl)); } public static void clearPreloadThreads() { executorService.shutdown(); executorService = Executors.newCachedThreadPool(); } private static class LoadTask extends Task<FXMLLoader> { private URL resource; private ControllerTask controllerTask = new ControllerTask(); public LoadTask(URL resource) { this.resource = resource; } public LoadTask(URL resource, Controller controller) { this.resource = resource; controllerTask.setController(controller); } @Override protected FXMLLoader call() throws Exception { FXMLLoader loader = new FXMLLoader(resource); executorService.submit(controllerTask); loader.setControllerFactory((aClass -> { try { Controller ret = controllerTask.get(); return ret; } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } throw new IllegalStateException(); })); loader.load(); return loader; } public ControllerTask getControllerTask() { synchronized (controllerTask) { return controllerTask; } } } private static class ControllerTask extends Task<Controller> { private Controller controller = null; private Thread thread; @Override protected Controller call() throws Exception { thread = Thread.currentThread(); while (controller == null) { try { Thread.sleep(1000); } catch (InterruptedException e) { } } return controller; } public Controller getController() { return controller; } public void setController(Controller controller) { this.controller = controller; set(controller); if (thread != null && thread.isAlive()) { thread.interrupt(); } } } }