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

时间:2015-08-23 19:36:26

标签: java javafx treeview

实际上,我正在JarEntry中循环JarFile个类,并尝试将每个包添加为TreeView<String>上的节点。

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

以下是我与之合作的一个例子:

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

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

    • 有机
      • 东西
        • 公地
        • 更多

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

老实说,这是我曾试图实现的更令人困惑的事情。除了创建某种形式的扩展treeitem类(作为条目包装器或类似的东西)之外,我无法想出这样做的好算法。

非常感谢任何指导。我目前的代码如下:

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.

1 个答案:

答案 0 :(得分:1)

我会创建一个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);
    }
}