/** * Copyright [2015] [Christian Loehnert] * * 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.markdown.viewer; 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.markdown.MarkdownParser; import javafx.beans.property.SimpleObjectProperty; 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.spi.CDI; import javax.inject.Inject; import java.io.File; import java.net.URL; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; public class MarkdownViewer implements Initializable, ActivityCallback { public static CompletableFuture<DefaultLoader<Node, MarkdownViewer>> load(Consumer<StackPane> viewConsumer, Consumer<MarkdownViewer> controllerConsumer) { ActivityInitialization initialization = CDI.current().select(ActivityInitialization.class).get(); return initialization.loadAdditionalControllerWithFuture(MarkdownViewer.class)// .thenApply(loader -> { viewConsumer.accept((StackPane) loader.getView()); controllerConsumer.accept(loader.getController()); return loader; }); } private static final Logger log = LoggerFactory.getLogger(MarkdownViewer.class); public static final String DEFAULT = "default"; protected final Map<String, String> preloaded = new ConcurrentHashMap<>(); protected final SimpleStringProperty currentHtml = new SimpleStringProperty(); protected final SimpleStringProperty currentIdentifier = new SimpleStringProperty(); @Inject ActivityController controller; @Inject MarkdownParser parser; @FXML protected StackPane root; protected WebView webView; protected final SimpleObjectProperty<File> file = new SimpleObjectProperty(); @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); }); } public void clear() { preloaded.clear(); } public void reset() { webView.getEngine().loadContent(""); currentIdentifier.set(""); } public void requestFocus() { webView.requestFocus(); } public void showDirect(String content) { currentIdentifier.set(DEFAULT); preloaded.remove(DEFAULT); preload(Collections.singletonList(new MarkdownContent(DEFAULT, content))); } public void showDirect(File content) { currentIdentifier.set(DEFAULT); preloaded.remove(DEFAULT); preload(Collections.singletonList(new MarkdownContent(DEFAULT, content))); } public void show(MarkdownContent 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.getMarkdown() == null ? "" : content.getMarkdown()); } } public void preload(Collection<MarkdownContent> load) { ActivityExecutor executorService = controller.getExecutorService(); JavaFXExecutorService javaFXExecutor = controller.getJavaFXExecutor(); load.forEach(t -> { CompletableFuture<Pair<String, String>> completableFuture = CompletableFuture.supplyAsync(() -> { if (t.hasFile()) { return parser.parse(t.getMarkdownFile()); } else { String desc = t.getMarkdown(); if (desc == null || desc.trim().isEmpty()) { return ""; } else { return parser.parse(desc, Optional.ofNullable(this.file.getValue())); } } })// .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; }); } ); } public String getCurrentHtml() { return currentHtml.get(); } public SimpleStringProperty currentHtmlProperty() { return currentHtml; } public void setCurrentHtml(String currentHtml) { this.currentHtml.set(currentHtml); } public WebView getWebView() { return webView; } public File getFile() { return file.get(); } public SimpleObjectProperty<File> fileProperty() { return file; } public void setFile(File file) { this.file.set(file); } }