package com.kodcu.service.extension; import com.kodcu.config.ExtensionConfigBean; import com.kodcu.controller.ApplicationController; import com.kodcu.other.Current; import com.kodcu.other.IOHelper; import com.kodcu.other.TrimWhite; import com.kodcu.other.Tuple; import com.kodcu.service.ThreadService; import com.kodcu.service.cache.BinaryCacheService; import com.kodcu.service.ui.AwesomeService; import javafx.embed.swing.SwingFXUtils; import javafx.scene.Node; import javafx.scene.SnapshotParameters; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.image.WritableImage; import javafx.scene.web.WebView; import netscape.javascript.JSObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.awt.image.BufferedImage; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * Created by usta on 25.12.2014. */ @Component public class TreeService implements DefaultSettings { private final Logger logger = LoggerFactory.getLogger(TreeService.class); private Current current; private ApplicationController controller; private ThreadService threadService; private AwesomeService awesomeService; private final BinaryCacheService binaryCacheService; private final ExtensionConfigBean extensionConfigBean; Pattern pattern = Pattern.compile("^(addw|minw|setw|addh|minh|seth|scale):\\s*(\\d+)$"); @Value("${application.treeview.url}") private String treeviewUrl; private WebView treeview; private boolean initialized; @Autowired public TreeService(final Current current, final ApplicationController controller, final ThreadService threadService, final AwesomeService awesomeService, BinaryCacheService binaryCacheService, ExtensionConfigBean extensionConfigBean) { this.current = current; this.controller = controller; this.threadService = threadService; this.awesomeService = awesomeService; this.binaryCacheService = binaryCacheService; this.extensionConfigBean = extensionConfigBean; } public void createFileTree(String tree, String type, String imagesDir, String imageTarget, String nodename) { Objects.requireNonNull(imageTarget); boolean cachedResource = imageTarget.contains("/afx/cache"); if (!imageTarget.endsWith(".png") && !cachedResource) return; Integer cacheHit = current.getCache().get(imageTarget); int hashCode = (imageTarget + imagesDir + type + tree).hashCode(); if (Objects.isNull(cacheHit) || hashCode != cacheHit) { logger.debug("Tree extension is started for {}", imageTarget); try { List<String> strings = Arrays.asList(tree.split("\\r?\\n")); Map<String, Integer> settings = newSettings(); List<TreeItem<Tuple<Integer, String>>> treeItems = strings.stream() .map(s -> { if (s.isEmpty()) { return null; } Matcher matcher = pattern.matcher(s); if (matcher.matches() && matcher.groupCount() == 2) { String command = matcher.group(1); String value = matcher.group(2); settings.put(command, Integer.valueOf(value)); return null; } if (!s.contains("#")) { return null; } int level = StringUtils.countOccurrencesOf(s, "#"); String value = s.replace(" ", "").replace("#", ""); return new Tuple<Integer, String>(level, value); }) .filter(Objects::nonNull) .map(t -> { Node icon = awesomeService.getIcon(Paths.get(t.getValue())); TreeItem<Tuple<Integer, String>> treeItem = new TreeItem<>(t, icon); treeItem.setExpanded(true); return treeItem; }) .collect(Collectors.toList()); TreeView fileView = getSnaphotTreeView(); Integer scale = settings.get("scale"); if (Objects.isNull(scale)) { scale = extensionConfigBean.getDefaultImageScale(); } fileView.setScaleX(scale); fileView.setScaleY(scale); for (int index = 0; index < treeItems.size(); index++) { TreeItem<Tuple<Integer, String>> currentItem = treeItems.get(index); Tuple<Integer, String> currentItemValue = currentItem.getValue(); if (index == 0) { fileView.setRoot(currentItem); continue; } TreeItem<Tuple<Integer, String>> lastItem = treeItems.get(index - 1); int lastPos = lastItem.getValue().getKey(); if (currentItemValue.getKey() > lastPos) { lastItem.getChildren().add(currentItem); continue; } if (currentItemValue.getKey() == lastPos) { TreeItem<Tuple<Integer, String>> parent = lastItem.getParent(); if (Objects.isNull(parent)) parent = fileView.getRoot(); parent.getChildren().add(currentItem); continue; } if (currentItemValue.getKey() < lastPos) { List<TreeItem<Tuple<Integer, String>>> collect = treeItems.stream() .filter(t -> t.getValue().getKey() == currentItemValue.getKey()) .collect(Collectors.toList()); if (collect.size() > 0) { TreeItem<Tuple<Integer, String>> parent = fileView.getRoot(); try { TreeItem<Tuple<Integer, String>> treeItem = collect.get(collect.indexOf(currentItem) - 1); parent = treeItem.getParent(); } catch (RuntimeException e) { logger.info(e.getMessage(), e); } parent.getChildren().add(currentItem); } continue; } } Path path = current.currentTab().getParentOrWorkdir(); int changeWidth = (settings.get("addw") - settings.get("minw")); int changeHeight = (settings.get("addh") - settings.get("minh")); threadService.runActionLater(() -> { controller.getRootAnchor().getChildren().add(fileView); if (settings.get("setw") > 0) { fileView.setPrefWidth(settings.get("setw")); } else { fileView.setPrefWidth(300 + changeWidth); } if (settings.get("seth") > 0) { fileView.setPrefHeight(settings.get("seth")); } else { fileView.setPrefHeight((treeItems.size() * 24) + 10 + changeHeight); } WritableImage writableImage = fileView.snapshot(new SnapshotParameters(), null); BufferedImage bufferedImage = SwingFXUtils.fromFXImage(writableImage, null); if (!cachedResource) { Path treePath = path.resolve(imageTarget); IOHelper.createDirectories(path.resolve(imagesDir)); IOHelper.imageWrite((BufferedImage) bufferedImage, "png", treePath.toFile()); controller.clearImageCache(treePath); } else { binaryCacheService.putBinary(imageTarget, (BufferedImage) bufferedImage); controller.clearImageCache(imageTarget); } logger.debug("Tree extension is ended for {}", imageTarget); controller.getRootAnchor().getChildren().remove(fileView); }); } catch (Exception e) { logger.error("Problem occured while generating Filesystem Tree", e); } } current.getCache().put(imageTarget, hashCode); } private TreeView getSnaphotTreeView() { TreeView fileView = new TreeView(); fileView.getStyleClass().add("tree-extension"); fileView.setLayoutX(-13000); fileView.setLayoutY(-13000); fileView.setMinSize(0, 0); return fileView; } public void createHighlightFileTree(String tree, String type, String imagesDir, String imageTarget, String nodename) { Objects.requireNonNull(imageTarget); boolean cachedResource = imageTarget.contains("/afx/cache"); if (!imageTarget.endsWith(".png") && !cachedResource) return; Integer cacheHit = current.getCache().get(imageTarget); int hashCode = (imageTarget + imagesDir + type + tree).hashCode(); if (Objects.isNull(cacheHit) || hashCode != cacheHit) { Path path = current.currentTab().getParentOrWorkdir(); threadService.runActionLater(() -> { WebView treeview = new WebView(); treeview.setLayoutX(-16000); treeview.setLayoutY(-16000); treeview.setMinSize(0, 0); int zoom = extensionConfigBean.getDefaultImageZoom(); treeview.setPrefSize(3000, 6000); treeview.setZoom(zoom); controller.getRootAnchor().getChildren().add(treeview); threadService.runActionLater(() -> { treeview.getEngine().load(String.format(treeviewUrl, controller.getPort())); }); treeview.getEngine().setOnAlert(event -> { String data = event.getData(); if ("READY".equals(data)) { ((JSObject) treeview.getEngine().executeScript("window")).call("executeTree", tree); } if ("RENDERED".equals(data)) { WritableImage writableImage = treeview.snapshot(new SnapshotParameters(), null); BufferedImage bufferedImage = SwingFXUtils.fromFXImage(writableImage, null); threadService.runTaskLater(() -> { TrimWhite trimWhite = new TrimWhite(); BufferedImage trimmed = trimWhite.trim(bufferedImage); if (!cachedResource) { Path treePath = path.resolve(imageTarget); IOHelper.createDirectories(path.resolve(imagesDir)); IOHelper.imageWrite(trimmed, "png", treePath.toFile()); controller.clearImageCache(treePath); } else { binaryCacheService.putBinary(imageTarget, trimmed); controller.clearImageCache(imageTarget); } threadService.runActionLater(() -> { controller.getRootAnchor().getChildren().remove(treeview); }); }); } }); }); } current.getCache().put(imageTarget, hashCode); } }