java - 如何使用 JarEntries 填充 JavaFX TreeView(包 - 文件)

标签 java javafx treeview

基本上,我正在循环 JarEntry来自 JarFile 的类(class)并尝试将每个包添加为 TreeView<String> 上的节点.

令人烦恼的是,退回的包裹被斜杠分开。含义:我必须将每个包名称拆分为一个数组,然后检查每个部分(包)是否已存在于树中。

这是我正在使用的示例:

org/something/commons org/something/commons/more

我需要以某种方式处理每个字符串来创建这种结构:

    • 组织
      • 某事
        • 公共(public)资源
        • 更多

之后,我需要将非目录条目中的每个类文件添加到每个预先存在的目录节点。

老实说,这是我尝试过实现的最令人困惑的事情。除了创建某种形式的扩展树项类来充当条目包装器或类似的东西之外,我只是想不出一个好的算法来执行此操作。

任何指导将不胜感激。我当前的代码如下:

private void populateTree(Enumeration<String> jarEntries) {

    jarFile.stream().forEach(entry -> {
        String entryName = entry.getName();
        if (entry.isDirectory()) {
            String[] packages = entryName.split("/");

            for(String packageName : packages) {
                // check if already exists in root node
                if(root.getChildren().contains(root.getChildren().indexOf(packageName))) {
                    TreeItem<String> packageNode = root.getChildren().get(root.getChildren().indexOf(packageName));
                    packageNode.getChildren().add(new TreeItem<String>(packageName));
                } else {
                    root.getChildren().add(new TreeItem<String>(packageName));
                }
            }

        } else {
            // it's a file
            String fileName = entryName.substring(entryName.lastIndexOf("/"), entryName.length());
            String[] packages = entryName.substring(0, entryName.lastIndexOf("/")).split("/");
            // somehow loop through each child of the root node and eventually, using some form of traversal algo, get to the package node to add new item to
        }
    });

    root.setExpanded(true);

}

这会产生以下错误输出: Incorrect output, it just adds the packages again and again to the root instead of building on top of already added, existing, package nodes.

最佳答案

我会创建一个TreeView<JarEntry> ,因此数据由 TreeItem 包裹是 JarEntry对象。然后使用 cellFactory指示单元格仅显示路径的最后一部分。

实际上填充树有点棘手,因为 jar 文件不需要包含其目录条目。因此,您最终可能必须在构建结构时创建新条目。我不确定我是否遵循您发布的方法:您不是将所有包及其子包直接添加到根目录(而不是子包到包)吗?

这是 SSCCE。您也许能够找到填充树的更清晰的实现...

import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.stage.Stage;


public class JarFileTreeView extends Application {

    @Override
    public void start(Stage primaryStage) {
        TreeView<JarEntry> tree = new TreeView<>();
        tree.setShowRoot(false);
        TreeItem<JarEntry> root = new TreeItem<>();
        tree.setRoot(root);

        // only display last portion of the path in the cells:
        tree.setCellFactory(tv -> new TreeCell<JarEntry>() {
            @Override
            public void updateItem(JarEntry item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                    setText(null);
                } else {
                    String[] pathElements = item.getName().split("/");
                    setText(pathElements[pathElements.length - 1]);
                }
            }
        });

        ObjectProperty<JarFile> jarFile = new SimpleObjectProperty<>();
        jarFile.addListener((obs, oldFile, newFile) -> {
            if (newFile == null) {
                root.getChildren().clear();
            } else {
                populateTree(root, newFile);
            }
        });

        FileChooser chooser = new FileChooser();
        chooser.getExtensionFilters().add(new ExtensionFilter("Jar Files", "*.jar"));
        Button loadButton = new Button("Load...");
        loadButton.setOnAction(e -> {
            File file = chooser.showOpenDialog(primaryStage);
            if (file != null) {
                try {
                    jarFile.set(new JarFile(file));
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        });

        BorderPane uiRoot = new BorderPane(tree, null, null, loadButton, null);
        BorderPane.setMargin(loadButton, new Insets(10));
        BorderPane.setAlignment(loadButton, Pos.CENTER);
        Scene scene = new Scene(uiRoot, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void populateTree(TreeItem<JarEntry> root, JarFile file) {
        root.getChildren().clear();
        List<JarEntry> entries = file.stream().collect(Collectors.toList());

        // sort by length of path (i.e. number of components separated by "/"), then by name:
        entries.sort(Comparator
            .comparing((JarEntry entry) -> entry.getName().split("/").length)
            .thenComparing(entry -> {
                String[] pathElements = entry.getName().split("/");
                return pathElements[pathElements.length - 1];
            }));

        for (JarEntry entry : entries) {

            // need to find correct parent for entry. That parent (or any of the ancestors)
            // might not exist yet, so we create it if necessary as we search.

            // Split name up into folder, subfolder, etc:
            List<String> pathElements = Arrays.asList(entry.getName().split("/"));

            // Search for parent. Start at root:
            TreeItem<JarEntry> parent = root;

            // Iterate through all elements except the last, traversing tree:
            for (int i = 0; i < pathElements.size() - 1 ; i++) {

                // name of ancestor entry:
                String matchingName = String.join("/", pathElements.subList(0, i+1));

                final TreeItem<JarEntry> current = parent ;

                // update parent with current parent's descendant, matching appropriate name:
                parent = current.getChildren().stream()
                    .filter(child -> child.getValue().getName().equals(matchingName))
                    .findFirst()
                    // it's possible this ancestor didn't yet exist, so we create it, 
                    // and add it to the correct parent:
                    .orElseGet(() -> {
                       JarEntry newEntry = new JarEntry(matchingName);
                       TreeItem<JarEntry> newItem = new TreeItem<>(newEntry);
                       current.getChildren().add(newItem);
                       return newItem ;
                    });
            }
            // after all that, we have a valid parent:
            parent.getChildren().add(new TreeItem<>(entry));
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

关于java - 如何使用 JarEntries 填充 JavaFX TreeView(包 - 文件),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32170723/

相关文章:

java - 每个单元格等于一个对象而不是行的表格

c# - 如何将一个 TreeView 实例的节点添加到同一 TreeView 的另一个实例

javascript - nickperkinslondon angular treeview expand_all() 问题

java - String.format 和 StringBuilder 之间的性能

java - 如何全局更改 JTable 行高?

textarea - JavaFX/ScalaFX - 更改禁用 TextArea 的文本颜色?

performance - 是什么让 JavaFx 1.2 场景图刷新?

asp.net - 如何获取TreeView中TreeNode的ClientID?

java - 接口(interface)的泛化导致编译错误

java - 为什么 IntelliJ 显示 "Duplicated code fragment (x lines long)"警告?