How do I get all nodes in a parent in JavaFX? - java

In C# I found a method that was pretty sweet that allowed you to get all the descendants and all of THEIR descendants from a specified control.
I'm looking for a similar method for JavaFX.
I saw that the Parent class is what I want to work with since it is the class from which all Node classes that bear children are derived.
This is what I have so far (and I haven't really found anything on google with searches like "JavaFX get all nodes from a scene"):
public static ArrayList<Node> GetAllNodes(Parent root){
ArrayList<Node> Descendents = new ArrayList<>();
root.getChildrenUnmodifiable().stream().forEach(N -> {
if (!Descendents.contains(N)) Descendents.add(N);
if (N.getClass() == Parent.class) Descendents.addAll(
GetAllNodes((Parent)N)
);
});
}
So how do I tell if N is a parent (or extended from a parent)? Am I doing that right? It doesn't seem to be working... It's grabbing all the nodes from the root (parent) node but not from the nodes with children in them. I feel like this is something that's probably got an answer to it but I'm just asking the question... wrong. How do I go about doing this?

public static ArrayList<Node> getAllNodes(Parent root) {
ArrayList<Node> nodes = new ArrayList<Node>();
addAllDescendents(root, nodes);
return nodes;
}
private static void addAllDescendents(Parent parent, ArrayList<Node> nodes) {
for (Node node : parent.getChildrenUnmodifiable()) {
nodes.add(node);
if (node instanceof Parent)
addAllDescendents((Parent)node, nodes);
}
}

I use this,
public class NodeUtils {
public static <T extends Pane> List<Node> paneNodes(T parent) {
return paneNodes(parent, new ArrayList<Node>());
}
private static <T extends Pane> List<Node> paneNodes(T parent, List<Node> nodes) {
for (Node node : parent.getChildren()) {
if (node instanceof Pane) {
paneNodes((Pane) node, nodes);
} else {
nodes.add(node);
}
}
return nodes;
}
}
Usage,
List<Node> nodes = NodeUtils.paneNodes(aVBoxOrAnotherContainer);
This source code uses the references of the existing nodes. It does not clone them.

This seems to get ALL nodes.
(In Kotlin)
fun getAllNodes(root: Parent): ArrayList<Node> {
var nodes = ArrayList<Node>()
fun recurseNodes(node: Node) {
nodes.add(node)
if(node is Parent)
for(child in node.childrenUnmodifiable) {
recurseNodes(child)
}
}
recurseNodes(root)
return nodes
}

I'd like to add to Hans' answer, that you have to check if parent is a SplitPane. Because SplitPanes have an empty list using getUnmodifiableChildren(), you'll have to use getItems() instead. (I do not know if there are other parents that do not provide their children via getUnmodifiableChildren(). SplitPane was the first I found...)

Unfortunately this won't get subnodes for most container components. If you try a TabPane as parent, you'll find no children, but you can find tabs in it with getTabs(). The same is with SplitPane and other. So every container will require a specific approach.
You could use node.lookupAll("*"), but it also doesn't look inside.
The solution could be a "Prototype" pattern - creating a meta class with common interface of getChildren() method, which is realized in subclasses - one for each type.
Approach example is given here.

This works for me:
public class FXUtil {
public static final List<Node> getAllChildren(final Parent parent) {
final List<Node> result = new LinkedList<>();
if (parent != null) {
final List<Node> childrenLvl1 = parent.getChildrenUnmodifiable();
result.addAll(childrenLvl1);
final List<Node> childrenLvl2 =
childrenLvl1.stream()
.filter(c -> c instanceof Parent)
.map(c -> (Parent) c)
.map(FXUtil::getAllChildren)
.flatMap(List::stream)
.collect(Collectors.toList());
result.addAll(childrenLvl2);
}
return result;
}
}

Related

Deep copy a recursive list in Java with circular references

I have the class:
public class Node
{
private String id;
private List<Node> children;
}
I need to create a deep copy of a List of it List, but given that there might circular references I was trying implementing the Cloneable interface and overriding the clone method but I keep getting Stackoverflow exception, so I wonder if there is a way to deep copy it that's fast and removes the circular dependencies in the process?
Class using cloneable, when I try to clone and it has circular references I get the error mention about
public class Node implements Cloneable
{
private String id;
private List<Node> children;
#Override
public Object clone() throws CloneNotSupportedException {
Node clone = (Node) super.clone();
if (children != null) {
List<Node> cloneChildren = new ArrayList<>(children.size());
for (Node child : children) {
cloneChildren.add((Node) child.clone());
}
clone.setChildren(cloneChildren);
}
return clone;
}
public List<Node> getChildren() {
return children;
}
public void setChildren(List<Node> children) {
this.children = children;
}
}
The easiest approach is to use an IdentityHashMap<Node,Node> to keep track of previously seen Nodes, mapping Nodes to be copied with Nodes copied thus far.
public Node copy() {
Map<Node,Node> copied = new IdentityHashMap<>();
return copy(copied, this);
}
private static Node copy(Map<Node,Node> copied, Node orig) {
Node existing = copied.get(orig);
if (existing != null) {
return existing;
}
Node copy = new Node();
copy.id = orig.id;
copy.children = new ArrayList<>();
copied.put(orig, copy);
for (Node child : orig.children) {
copy.children.add(copy(copied, child));
}
return copy;
}
(As usual, code compiled but not tested.)
I was thinking there is a 'clever' approach using Floyd's Tortoise and Hare Algorithm, but this is complicated by it not being a singly-linked list.

Recursive TreeView creation with the data provided in this question?

I have a Treeview TreeView<MyType> which I'd like to fill recursively from a MyType root object. The structure of the class MyType is the following:
public class MyType {
private Set<MyType> children = new HashSet<>();
public Set<MyType> getChildren() {
return children;
}
public void setChildren(Set<MyType> children) {
this.children = children;
}
}
So as you can see, the MyType root/parent has children of the same type, and those children can also have children from the same type. In practice, the depth between the root and its furthermost inheritant is no greater than 1000 level.
I want to fill the Treeview TreeView<MyType> recursively with tree items TreeItem<MyType>in the same tree structure as the data is stored in the MyType root file.
This is what I've tried so far but it's not working:
void buildTree(MyType parent, TreeItem<MyType> result) {
for (MyType child : parent.getChildren()) {
if (child.getChildren() == null || child.getChildren().isEmpty()) {
result.getChildren().add(new TreeItem<MyType>(child));
}
else {
TreeItem<MyType> tmp = new TreeItem<>(child);
buildTree(child, tmp);
}
}
}
Is it possible to make the filling work with the data structure provided?
It's more convenient to
A. Return TreeItems instead of passing both MyType and TreeItem to the recursive method calls.
B. Treat leafs as terminal cases instead of handling terminal cases at parents of leafs
This allows you to write the following code:
private TreeItem<MyType> buildSubtree(MyType root) {
TreeItem<MyType> result = new TreeItem<>(root);
if (root.getChildren() != null) {
for (MyType child : root.getChildren()) {
result.getChildren().add(buildSubtree(child));
}
}
return result;
}
Ok, I just figured it out. This is working as expected.
void buildTree(MyType parent, TreeItem<MyType> result) {
for (MyType child : parent.getChildren()) {
if (child.getChildren() == null || child.getChildren().isEmpty()) {
result.getChildren().add(new TreeItem<MyType>(child));
}
else {
TreeItem<MyType> tmp = new TreeItem<>(child);
buildTree(child, tmp);
result.getChildren().add(tmp);
}
}
}

Adding new layer of children in a Tree

I'm making a Tree and have been able to implement setting the Parent and child properly. I'm stuck at attempting to make a new layer of the tree, a grandchild in this case. It isn't a binary tree. Ideally I'd like to place the implementation of the new layer in either the constructor or in the addChild method. The following is what I have for my set up and the methods setting up my Parent and Children in my Tree
import java.util.ArrayList;
import java.util.List;
public abstract class TreeNode {
static int count; // store # tree nodes created. Generate IDs
private String id;// unique id # of nodes.
private List<TreeNode> children; // store all children of node
private TreeNode parent; // reference parent of the node. Null is node is root
public TreeNode(List<TreeNode> children) {
this.children = children;
this.count++;
this.id = Integer.toString(this.count);
int ctr = 0;
if (children != null) {
while (ctr < children.size()) {
children.get(ctr).setParent(this);
ctr++;
}
}
}
public void addChild(TreeNode child) { // add single child to current node
if (this.getChildren() == null) {
child.setParent(this);
this.setChildren(new ArrayList<TreeNode>());
this.children.add(child);
}
else {
child.setParent(this);
this.children.add(child);
}
}
Why is the class abstract? (I can't see an intent or a reason to extend it.)
Perhaps I don't understand what you're asking; from where I'm sitting, you already have support for "a new layer of the tree" -- as many as you want, in fact (at least of the class weren't abstract)...
TreeNode grandparent = new TreeNode(new ArrayList<TreeNode>()); // I recommend a zero-arg constructor that does this
TreeNode child = new TreeNode(new ArrayList<TreeNode>());
grandparent.addChild(child);
TreeNode grandchild = new TreeNode(new ArrayList<TreeNode>());
child.addChild(grandchild);
You now have a 3-level tree.
More Suggestions
Your id field
What is the purpose of the id filed? As you have it, you could get duplicates in a multi-threaded context. Better would be...
this.id = UUID.randomUUID().toString();
Every TreeNode would receive a unique id for certain (though they wouldn't be numeric or sequential).
Also, id values based on UUID will be unique even if you restart your VM and you're using persistent TreeNode objects.
The loop in your constructor
Better than...
while (ctr < children.size()) {
children.get(ctr).setParent(this);
ctr++;
}
would be...
for (TreeNode child : children) {
child.setParent(this);
}

How to recursively traverse and store a dir structure?

How would I go around recursively traversing and storing a directory structure? I'm writing an app to load up two folders, traverse their structure, show the differences in a GUI, and synchronize the two if prompted to do so.
Background: I get the basic principle(use a tree; if a file, append it to the calling dir, if a dir, append and recursively call the function on it), but all the implementations I found are just a little bit off from what I need, or they spit out a NullPointerException which I can't fix, as I don't understand what exactly they do. I think I'm stuck on a technicality or a quirk somewhere in the recursive calls or in creating child nodes.
Edit:
This is a node class i copied from somewhere. I recently deleted my old putIntoTree function, and I'm currently working on a new one. I'm stuck on how exactly the children/parent creation works.
import java.nio.file.Path;
import java.util.*;
abstract class TreeNode implements Iterable<TreeNode> {
private Set<TreeNode> children;
public Path path;
public TreeNode(Path path) {
children = new HashSet<TreeNode>();
this.path = path;
}
public boolean addChild(TreeNode n) {
return children.add(n);
}
public boolean removeChild(TreeNode n) {
return children.remove(n);
}
public Iterator<TreeNode> iterator() {
return children.iterator();
}
}
Edit: I'm trying to get this tree structure to work in there, but I'm a bit baffled at how exactly pointy brackets and generics work. It says "cannot resolve symbol[whatever is after the in my own attempt at defining a method]"
public class Tree<T> {
private Node<T> root;
public Tree(T rootData) {
root = new Node<T>();
root.data = rootData;
root.children = new ArrayList<Node<T>>();
}
public static class Node<T> {
private T data;
private Node<T> parent;
private List<Node<T>> children;
}
}

How to add a child to a specific node in n-array tree?

I wrote this n-array tree class. I want to write a method to add a child to a specific node in my tree.
First I should search my tree to find the father then add the child to that node children.
I don't know how I should declare my method
public class FamilyNode {
public String name;
public String Family;
public String sex;
public FamilyNode Father;
public FamilyNode Mother;
public FamilyNode Spouse=null;
public String status="alive";
public int population;
public ArrayList<FamilyNode> children=new ArrayList<FamilyNode>() ;
public FamilyNode(String firstname,String lastname,String sex1){
this.name=firstname;
this.Family=lastname;
this.sex=sex1;
this.population=this.children.size()+1;
}
public void SetParents(FamilyNode father,FamilyNode mother){
this.Father=father;
this.Mother=mother;
}
public void SetHW(FamilyNode HW){
this.Spouse=HW;
}
public int Number (){
int number_of_descendants = this.population;
if(this.Spouse!=null) number_of_descendants++;
for(int index = 0; index < this.children.size(); index++)
number_of_descendants = number_of_descendants+ this.children.get(index).Number();
return number_of_descendants;
}
public void AddChild(FamilyNode Father,FamilyNode child){
//the code here
}
}
I answered one of your related questions yesterday so let's continue with the code I posted :)
public class FamilyNode {
// ...
// ...
public FamilyNode findNodeByName(String nodeName){
if(name.equals(nodeName)){
// We found a node named nodeName, return it
return this;
}
// That's not me that you are looking for, let's see my kids
for(FamilyNode child : children){
if(child.findNodeByName(nodeName) != null)
// We found what we are looking, just return from here
return child;
}
// Finished looping over all nodes and did not find any, return null
return null;
}
public void addChild(FamilyNode child){
children.add(child);
}
}
Basically, you need to find the node you are looking for (by name in this case) and that can be done by the findNodeByName above. Once the node is found, add one child to it.
Use this code like this:
FamilyNode root = ...;
FamilyNode node = root.findNodeByName("Parent");
if(node != null) node.addChild(...);
NOTE
If you want to debug and visit all your tree nodes, use this method:
public FamilyNode findNodeByName(String nodeName){
System.out.println("Visiting node "+ name);
// That's not me that you are looking for, let's see my kids
for(FamilyNode child : children){
child.findNodeByName(nodeName)
}
// Finished looping over all nodes and did not find any, return null
return null;
}
This isn't exactly a tree, as children have potentially two parents rather than just one. It's a directed graph.
It would be good to change your variable and method names to be consistent with the usual Java convention of starting with a lowercase character.
In the interest of data consistency, you might consider making the addChild method something that simply adds to the list of children for the current node, but in your setParents method, update the child lists of both parents, adding the current node as a child there, by calling father.addChild(this) and mother.addChild(this) (protecting against them being null of course).
If the parents can be changed when they're previously set (presumably in error), you'll also need to remove the current node from previously set parents. For this, you might need a removeChild(FamilyNode child) method. Again for data consistency, this method should probably also set the appropriate parent field in the child node to null.

Categories

Resources