Is there any way to get a TreeView branch by name?
For example, if I have a TreeView menu like so:
TreeItem<String> root, branch;
root = new TreeItem<>();
root.setExpanded(true);
branch = makeBranch("Chicken", root);
makeBranch("Hen", branch);
Here is the makeBranch() method:
public TreeItem<String> makeBranch(String s, TreeItem<String> parent){
TreeItem<String> item = new TreeItem<>(s);
item.setExpanded(true);
parent.getChildren().add(item);
return item;
}
Now if I want to get a branch, I can do:
branch.getChildren().get(a_number); // get using index
But is there anyway I can get a branch by its name rather than value?
So something like:
branch.getChildren().get("the name of leaf or branch");
Since TreeItem.getChildren() returns a ObservableList<TreeItem<T>>, you can use the methods of List to find the child you're looking for, e.g. using Stream to filter the content:
final String value = "the name of leaf or branch";
Optional<TreeItem<String>> nodeOptional = branch.getChildren().stream()
.filter(
(child)-> child.getValue().equals(value)
)
.findFirst();
if (nodeOptional.isPresent()) {
TreeItem<String> item = nodeOptional.get();
// do something with item
} else {
// no child with specified value was found
}
Note that you only search the direct children of branch that way. If you want to find arbitrary descendants of branch, you have to do a tree search.
Related
I want to move a tree from database in java.
First I get ResultSet from DB:
String selectTree = "select id, id_parent, text from tree ";
ResultSet resultSet = statement.executeQuery(selectTree);
resultSet.last();
sizeOfSelect = resultSet.getRow();
resultSet.beforeFirst();
list = new ArrayList<Node>(sizeOfSelect);
while(resultSet.next()) {
list.add(new Node(resultSet.getInt(1),
resultSet.getInt(2),
resultSet.getString(3)));
}
Now I have an array of nodes:
class Node<T> {
private int id;
private int parent_id;
private String text;
Node(int _id, int _parentId, String _text) {
this.id = _id;
this.parent_id = _parentId;
this.text = _text;
}
//getters & setters here
How can I obtain the same tree in java?
Could you tell me the line of thought, please.
I have made progress in solution.
I turning array of RawNode in map thus:
for(RawNode rawNode : tmpList) {
tmpMap.put(rawNode.getId(), dependsId(rawNode.getId()));
}
Where method dependsId() is:
private List<RawNode> dependsId(int id) {
List<RawNode> tmpDependList = new ArrayList<>();
for(RawNode rawNode : tmpList) {
if(rawNode.getParentId() == id) {
tmpDependList.add(rawNode);
}
}
return tmpList;
}
What should I do next? How transform this map in array of Node with parent and children?
Right now, your "raw" Nodes look like this:
class RawNode {
private int id;
private int parent_id;
private String text;
[ hint: I renamed that class declaration; and removed the <T> part which doesn't make any sense given the fact that you have nothing generic in that class; at least from what you showing ]
and ideally, that would mean that there is exactly ONE Node object for each ID. In other words; over time, you created objects like:
RawNode rootWithoutParent = new Node(0, -1, "root");
RawNode firstChildParent = new Node(1, 0, "root");
And you have all these objects in an array/list. Now you could transform those RawNodes, into "real" Nodes, and that one could look like:
class Node {
private int id;
private Node parent;
private List<Node> children = new ArrayList<>();
private String text;
Translation could be done in multiple iterations.
First, you create one Node per RawNode (and for later, we remember specific information in two maps):
List<Node> nodes = new ArrayList<>();
Map<Node, RawNode> rawNodesByNode = new HashMap<>();
Map<Integer, Node> nodesById = new HashMap<>();
for(RawNode rawNode : tmpList) {
Node node = new Node();
node.setId(rawNode.getId());
node.setText(rawNode.getText());
rawNodesByNode.put(node, rawNode);
nodesById.put(rawNode.getId(), node);
}
What is missing?
First, the "link" from childs to their parent.
for(Node node : nodes) {
RawNode raw = rawNodesByNode.get(node);
int parentId = raw.getParentId();
Node parent = nodesById.get(parentId);
node.setParent(parent);
}
Second ("optional") the "link" from parents to all childs:
for(Node node : nodes) {
Node parent = node.getParent(); // should be != null by now
parent.addChild(node);
}
with:
void addChild(Node node) {
children.add(node);
}
( please note: the children list is created directly when you do a Node.new() call; but at that point, it is empty. And beyond that: I didn't put my code into a compiler; it is meant as "pseudo code", to get you going; and give some insights into the required steps. There is also a certain potential for "bugs" - conditions where you need special checking to handle cases like the "root node" which doesn't have a parent; in other words: don't just copy&paste my code: understand what it is supposed to do and work from there).
Hope that gives you enough ideas to get you going. And please note: all of this is just "work"; there is no "rocket science" involved; so the essential part is that you now engage and understand what you are about to do. So resist the urge to ask yet-another question; instead: start working.
Well, if you want tree, your Node definitely lacks some links to other nodes(for parent at least), i'd suggest replacing parent_id in Node to Node parentNode, and set it according to parent_id from database, for example add them all to map at first, mapped by Map<Integer, Node> someMap, where integer is id, then when iterate trhough each and do:
list.add(new Node(resultSet.getInt(1),
someMap.get(resultSet.getInt(2)),
resultSet.getString(3)));
oh, and don't forget to check if that parent id is not null.
I don't really use result set often, but i suppose it would be better to move them from there to some temporary list.
Really sorry, don't have time to write everything as it should be, but i hope you've got my idea
specifically I need to represent the following:
The tree at any node can have an arbitrary number of children
Each parent node (after the root) is just a String (whose children are also Strings)
I need to be able to get parent and list out all the children (some sort of list or array of Strings) given an input string representing a given node
Dynamically populating the tree structure based on reference relationship between parent and child.
Example given is I have one member1 sponsor another member2, and member2 sponsor member 3 and so and so for. Already have the table records relationship
Is there an available structure for this ???
My data is from DB or a List, I will loop through the information with the name and the relation to determine if the node is a root, parent or a child.
So during the loop, I found a child, I need a reference to the parent so that I can compare the child relation to the parent before adding the child to its parent.
The closest code I found .
public class TreeNode<T> implements Iterable<TreeNode<T>> {
T data;
TreeNode<T> parent;
List<TreeNode<T>> children;
public TreeNode(T data) {
this.data = data;
this.children = new LinkedList<TreeNode<T>>();
}
public TreeNode<T> addChild(T child) {
TreeNode<T> childNode = new TreeNode<T>(child);
childNode.parent = this;
this.children.add(childNode);
return childNode;
}
// other features ...
}
Sample usage:
TreeNode<String> root = new TreeNode<String>("root");
{
TreeNode<String> node0 = root.addChild("node0");
TreeNode<String> node1 = root.addChild("node1");
TreeNode<String> node2 = root.addChild("node2");
{
TreeNode<String> node20 = node2.addChild(null);
TreeNode<String> node21 = node2.addChild("node21");
{
TreeNode<String> node210 = node20.addChild("node210");
}
}
}
This is what I have done so far. The parent will get overwritten by the latest entry so hence I am unable to retrieve what I have added previously .
public static TreeNode<String> getSet1() throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("test.txt"));
String line;
while ((line = reader.readLine()) != null) {
String[] items = line.split(":");
String name = items[0];
String parent = items[1];
String type = items[2];
if (parent.equalsIgnoreCase("-") && type.equalsIgnoreCase("mainparent")) {
root = new TreeNode<String>(name);
} else if (type.equalsIgnoreCase("ChildParent") && parent.equalsIgnoreCase(root.toString())) {
childParent = root.addChild(name);
} else if (type.equalsIgnoreCase("Child") && parent.equalsIgnoreCase(childParent.toString())) {
child = childParent.addChild(name);
}
}
return root;
}
Your diagram indicates a tree of arbitrary depth, but your code only handles grandparent -> parent -> child relationships (with a single grandparent at the root).
I would ignore the type, as all you need is the name of a person and the name of their parent. If the parent name is a dash, you know you have the root.
Now for each person, you need to get the parent node already in the tree (assuming parents come before children in the list - if that's not the case, the problem becomes significantly more complex, as you would have to store orphaned persons temporarily and for each new person see if they are the parent of an orphaned person).
In order to get the parent by name, you should store each person you have already processed in a second data structure, parallel to the tree. The second data structure should make it easy to look someone up by name. Maps, and in particular Hashtables, are ideal for this. This is how it works:
Map processedPersonsMap=new Hashtable<String, TreeNode<String>>();
For each person, you store them in the map, indexed by their name:
TreeNode<String> person=...;
processedPersonsMap.put(person.getData(), person);
When you read in a new person and their parent's name is not a dash, you look up the parent:
String parentName=items[1];
TreeNode<String> parent=processedPersonsMap.get(parentName);
In this way, no matter how deep the tree is, you will always find the right parents. However, keep in mind that this requires a valid input file where each child comes after their parent, and which does not contain circular references or missing parents.
If those conditions are not met, you have to handle them explicitly.
I'm fairly new to the Java 8 Streams API stuff, but I've decided to use it for a new piece of functionality but have hit a brick wall!
I have a bunch of MenuItem objects:
MenuItem parent1 = new MenuItem(0L, "Code Parent", "Description Parent");
MenuItem item1 = new MenuItem(1L, "Code1", "Description1");
MenuItem item2 = new MenuItem(2L, "Code2", "Description2");
MenuItem item3 = new MenuItem(3L, "Code3", "Description3");
MenuItem item4 = new MenuItem(4L, "Code4", "Description4");
I also have a bunch of MenuHierarchy objects which represent the hierarchical relationships between MenuItem (parent/child). This model is fixed as is, so have to work with what I've got.
Constructor - MenyHierarchy(id, parent, child, displayOrder)
MenuHierarchy hierarchy1 = new MenuHierarchy(1L, null, parent1);
MenuHierarchy hierarchy2 = new MenuHierarchy(2L, parent1, item1, 1);
MenuHierarchy hierarchy3 = new MenuHierarchy(3L, item1, item2, 2);
MenuHierarchy hierarchy4 = new MenuHierarchy(4L, item2, item3, 3);
MenuHierarchy hierarchy5 = new MenuHierarchy(5L, item3, item4, 4);
MenuHierarchy objects will a null parent are considered root nodes.
Now using the streams API I want to transform this relationship into a Tree like structure using a MenuNode entity that I've created:
public class MenuNode implements GenericNode<MenuItem> {
private MenuItem data;
private List<GenericNode<MenuItem>> children;
public MenuNode(MenuItem data) {
this.data = data;
this.children = new ArrayList<GenericNode<MenuItem>>();
}
// Getters, setters
}
I'll explain what I have so far:
/* This is the list of Root Menus (Menus which have no parent) */
List<MenuNode> rootNodes = new ArrayList<>();
List<MenuHierarchy> hierarchyList = Arrays.asList(hierarchy1, hierarchy2, hierarchy3, hierarchy4, hierarchy5);
/* This first stream adds a new root MenuNode object to the above list ordered by the hierarchy display order */
hierarchyList.parallelStream()
.filter((h) -> Objects.isNull(h.getParentMenu()))
.sorted((h, i) -> h.getDisplayOrder().compareTo(i.getDisplayOrder()))
.map(MenuHierarchy::getChildMenu)
.forEachOrdered((i) -> rootNodes.add(new MenuNode(i)));
/* This second one is where i've sort of failed...
What i need this to do is iterate over the menu hierarchies and for each non-root one
add it to the MenuNode children collection where MenuNode.data == MenyHeirarchy.parentMenu
Resulting in a tree of MenuItems...
*/
hierarchyList.stream()
.filter((h) -> Objects.nonNull(h.getParentMenu()))
.sorted((h, i) -> h.getDisplayOrder().compareTo(i.getDisplayOrder()))
.forEachOrdered((h) -> {
rootNodes.stream()
.filter((n) -> n.getData().equals(h.getParentMenu()))
.forEach((n) -> {
n.getChildren().add(new MenuNode(h.getChildMenu()));
});
});
As you can see this doesn't work properly at the moment as it doesn't represent all of the hierarchy... I'm not sure if this is even possible with streams?
Any ideas will be greatly recommended.
Do I understand correctly that you want to end up with a bunch of MenuNode objects that represent the menu? If that's the case, I think it would be easier to premake all the node objects and then populate their children list.
// first we make all the nodes and map them to ID
Map<Long, MenuNode> nodes = hierarchies.stream()
.map(MenuHierarchy::getChildMenu)
.collect(toMap(MenuItem::getId, MenuNode::new));
// and now we go over all hierarchies and add children to appropriate node
hierarchies.stream()
.filter(h -> h.getParent() != null)
.sorted(comparing(MenuHierarchy::getDisplayOrder))
.forEach(h -> {
long parentId = h.getParentMenu().getId();
long childId = h.getChildMenu().getId();
nodes.get(parentId).getChildren().add(nodes.get(childId))
});
Alternatively, the second part can be written by going over nodes first. The advantage of doing it this way is that you can make the child list in MenuNode immutable. The downside is that you might find the idea of repeatedly iterating over all hierarchies distasteful (even though it shouldn't matter for any realistic menu size):
nodes.values().forEach( node ->
node.setChildren(
hierarchies.stream()
.filter(h -> h.getParentMenu().getId() == node.getData().getId())
.sorted(comparing(MenuHierarchy::getDisplayOrder))
.map(MenuHierarchy::getChildMenu)
.map(MenuItem::getId)
.map(nodes::get)
.collect(toList())
)
);
And, for completeness sake, you can group up the hierarchies for the same parent using streams and rewrite the second part like this:
hierarchies.stream()
.filter(h -> null != h.getParent())
.collect(
groupingBy(h->g.getParentMenu().getId(), toList())
) // now we have a map of parent Ids to list of MenuHierarchy for that parent
.forEach( (parentId, children) ->
nodes.get(parentId)).setChildren(
children.stream()
.sorted(comparing(MenuHierarchy::getDisplayOrder))
.map(h -> nodes.get(h.getChildMenu().getId()))
.collect(toList())
)
);
You decide what's clearer to you.
Edit: I wasn't sure if hierarchy id is always the same as the child id. If it is, the code can be simplified a bit.
I have a JTree and an awt.Canvas. When i select multiple objects from within the Canvas into the objList, I want all the selected items to be shown inside the JTree as selected. That means for example if I have 2 objects selected, both their paths to root should be expanded, and also each selected object should have its corresponding TreeNode selected. My JTree has TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION.
Here is a sample of the expand funcion i use :
public void selectTreeNodes() {
HashMap <String, MyEntity> entities = ...;
Iterator it = entities.keySet().iterator();
while (it.hasNext()) {
String str = it.next().toString();
MyEntity ent = entities.get(str);
if (ent.isSelected()) {
DefaultMutableTreeNode searchNode = searchNode(ent.getName());
if (searchNode != null) {
TreeNode[] nodes = ((DefaultTreeModel) tree.getModel()).getPathToRoot(searchNode);
TreePath tpath = new TreePath(nodes);
tree.scrollPathToVisible(tpath);
tree.setSelectionPath(tpath);
}
}
}
}
public DefaultMutableTreeNode searchNode(String nodeStr)
{
DefaultMutableTreeNode node = null;
Enumeration enumeration= root.breadthFirstEnumeration();
while(enumeration.hasMoreElements()) {
node = (DefaultMutableTreeNode)enumeration.nextElement();
if(nodeStr.equals(node.getUserObject().toString())) {
return node;
}
}
//tree node with string node found return null
return null;
}
In my current state, if I select a single object, it will be selected in the JTree and its TreePath will be shown.
But if entities has more than 1 object selected, it will display nothing, my JTree will remain unchanged.
You are looking for the TreeSelectionModel of the JTree (use the getter). Use the TreeSelectionModel#setSelectionPaths for multiple paths. Now you are only setting one node selected due to your tree.setSelectionPath(tpath); call. The TreeSelectionModel also has methods to add/remove to an existing selection ,... (basically everything you might need in the future).
An interesting method for the expansion is the JTree#setExpandsSelectedPaths method which allows to configure the JTree to automatically expand selected paths. If you want to manage this manually, you can use the JTree#setExpandedState method
I have a table with fields category_id, category_name and parent_category_id. And parent_category_id has values from category_id which represents the parent child relationship. I dont have any fixed level of hierarchy, it may go up to 5 levels or 10 levels and there is no limit to that.. I need a code for how to implement this JTree to make thing work for me. I should be able to implement the same for Menu bar as well.. Please help me with this..
After googling I found this,
Map<String, Node> idToNode = new HashMap<String, Node>();
//create nodes from ResultSet
while ( resultSet.next() ){
Node node = //create node -contains info, parent id, and its own id from ResultSet
//put node into idToNode, keyed with its id
}
//link together
Iterator<String> it = idToNode.keySet().iterator();
Node root = null;
while ( it.hasNext() ){
Node node = idToNode.get(it.next());
Node parent = idToNode.get(node.getParentId());
if ( parent == null ) {
root = node;
}else{
parent.addChild(node);
}
}
How do i code those commented instructions?
Use DefaultMutableTreeNode to create your nodes
Make a map of IDs to nodes - as you get your nodes from the database, store them in the map with the id as their key.
Once you have all your nodes, go through them once more and match their parent ids up, retrieving them from the map.
Assuming your tree is structurally sound in the database, it will be sound here. Pick any node and follow the parent chain the the root.
With the root object, you can create your JTree. :)