/* * Copyright [2014] [Christian Loehnert, krampenschiesser@gmail.com] * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.ks.text.view; import de.ks.activity.ActivityController; import de.ks.activity.executor.ActivityExecutor; import de.ks.activity.initialization.ActivityCallback; import de.ks.activity.initialization.ActivityInitialization; import de.ks.application.fxml.DefaultLoader; import de.ks.executor.JavaFXExecutorService; import de.ks.text.AsciiDocParser; import de.ks.text.process.AsciiDocPreProcessor; import javafx.beans.property.SimpleStringProperty; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.Node; import javafx.scene.control.Control; import javafx.scene.layout.StackPane; import javafx.scene.web.WebView; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.enterprise.inject.Instance; import javax.enterprise.inject.spi.CDI; import javax.inject.Inject; import java.net.URL; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; public class AsciiDocViewer implements Initializable, ActivityCallback { public static final String DEFAULT = "default"; public static CompletableFuture<DefaultLoader<Node, AsciiDocViewer>> load(Consumer<StackPane> viewConsumer, Consumer<AsciiDocViewer> controllerConsumer) { ActivityInitialization initialization = CDI.current().select(ActivityInitialization.class).get(); return initialization.loadAdditionalControllerWithFuture(AsciiDocViewer.class)// .thenApply(loader -> { viewConsumer.accept((StackPane) loader.getView()); controllerConsumer.accept(loader.getController()); return loader; }); } private static final Logger log = LoggerFactory.getLogger(AsciiDocViewer.class); protected final Map<String, String> preloaded = new ConcurrentHashMap<>(); protected final List<AsciiDocPreProcessor> preProcessors = new ArrayList<>(); @Inject ActivityController controller; @Inject AsciiDocParser parser; @Inject Instance<AsciiDocPreProcessor> preProcessorProvider; @FXML protected StackPane root; protected WebView webView; protected final SimpleStringProperty currentHtml = new SimpleStringProperty(); protected final SimpleStringProperty currentIdentifier = new SimpleStringProperty(); protected Set<Runnable> supensionRunnables = new HashSet<>(); @Override public void initialize(URL location, ResourceBundle resources) { CompletableFuture.supplyAsync(() -> new WebView(), controller.getJavaFXExecutor()).thenAccept(view -> { webView = view; webView.setMinSize(100, 100); webView.setPrefSize(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE); root.getChildren().add(webView); }); Iterator<AsciiDocPreProcessor> iterator = preProcessorProvider.iterator(); while (iterator.hasNext()) { AsciiDocPreProcessor next = iterator.next(); addPreProcessor(next); } } public void reset() { webView.getEngine().loadContent(""); currentIdentifier.set(""); } public void showDirect(String content) { currentIdentifier.set(DEFAULT); preloaded.remove(DEFAULT); preload(Collections.singletonList(new AsciiDocContent(DEFAULT, content))); } public void show(AsciiDocContent content) { String identifier = content.getIdentifier(); currentIdentifier.set(identifier); if (preloaded.containsKey(identifier)) { String html = preloaded.get(identifier); currentHtml.set(html); webView.getEngine().loadContent(html == null ? "" : html); } else { preload(Collections.singletonList(content)); webView.getEngine().loadContent(content.getAdoc() == null ? "" : content.getAdoc()); } } public void preload(Collection<AsciiDocContent> load) { ActivityExecutor executorService = controller.getExecutorService(); JavaFXExecutorService javaFXExecutor = controller.getJavaFXExecutor(); load.forEach(t -> { CompletableFuture<Pair<String, String>> completableFuture = CompletableFuture.supplyAsync(() -> preProcess(t.getAdoc()), executorService)// .thenApply(desc -> { if (desc == null || desc.trim().isEmpty()) { return ""; } else { return parser.parse(desc); } })// .thenApply(html -> Pair.of(t.getIdentifier(), html)); completableFuture.thenApply(pair -> { preloaded.put(pair.getKey(), pair.getValue()); return pair; }).thenAcceptAsync(pair -> { if (currentIdentifier.getValueSafe().equals(pair.getKey())) { String html = pair.getValue(); currentHtml.set(html); webView.getEngine().loadContent(html); } }, javaFXExecutor)// .exceptionally(e -> { log.error("Could not parse adoc", e); return null; }); }); } protected String preProcess(String input) { for (AsciiDocPreProcessor preProcessor : preProcessors) { input = preProcessor.preProcess(input, this); } return input; } public void addPreProcessor(AsciiDocPreProcessor processor) { this.preProcessors.add(processor); } public void clear() { preloaded.clear(); } public void requestFocus() { webView.requestFocus(); } public WebView getWebView() { return webView; } public String getCurrentHtml() { return currentHtml.get(); } public SimpleStringProperty currentHtmlProperty() { return currentHtml; } public void setCurrentHtml(String currentHtml) { this.currentHtml.set(currentHtml); } public void addSuspensionRunnable(Runnable r) { supensionRunnables.add(r); } @Override public void onSuspend() { supensionRunnables.forEach(r -> r.run()); } @Override public void onStop() { onSuspend(); } }