如何使用 JarEntries(包 - 文件)填充 JavaFX TreeView
How to populate JavaFX TreeView using JarEntries (packages - files)
基本上,我从 JarFile
循环遍历 JarEntry
classes 并尝试将每个包添加为 TreeView<String>
.[=16 上的节点=]
令人讨厌的是,返回的包被斜杠分开了。意思是:我必须把每个包名拆分成一个数组,然后检查每个部分(包)是否已经存在于树中。
这是我正在使用的示例:
org/something/commons
org/something/commons/more
我需要以某种方式使用这些字符串中的每一个来创建这种结构:
- 根
- 组织
- 某事
- 公地
- 更多
之后,我需要将每个 class 文件从非目录条目添加到每个预先存在的目录节点。
老实说,这是我试图实现的最令人困惑的事情。除了创建某种形式的扩展 treeitem class 作为入口包装器或类似的东西之外,我只是想不出一个好的算法。
任何指导将不胜感激。我当前的代码是这样的:
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);
}
这会产生不正确的输出:
我会创建一个 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);
}
}
基本上,我从 JarFile
循环遍历 JarEntry
classes 并尝试将每个包添加为 TreeView<String>
.[=16 上的节点=]
令人讨厌的是,返回的包被斜杠分开了。意思是:我必须把每个包名拆分成一个数组,然后检查每个部分(包)是否已经存在于树中。
这是我正在使用的示例:
org/something/commons
org/something/commons/more
我需要以某种方式使用这些字符串中的每一个来创建这种结构:
- 根
- 组织
- 某事
- 公地
- 更多
- 某事
- 组织
之后,我需要将每个 class 文件从非目录条目添加到每个预先存在的目录节点。
老实说,这是我试图实现的最令人困惑的事情。除了创建某种形式的扩展 treeitem class 作为入口包装器或类似的东西之外,我只是想不出一个好的算法。
任何指导将不胜感激。我当前的代码是这样的:
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);
}
这会产生不正确的输出:
我会创建一个 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);
}
}