I have a Swing application that uses a JTree. I want some of the nodes of the tree to be hidden, so I have implemented two DefaultTreeModels, one with every node and a filtered one with only the ones that should be displayed. The latter is set as the actual model.
At some points I must change the filtered nodes, and when I do, the items in the tree update properly, but their behavior is wrong. Nodes do not get highlighted when they are selected (even though they are indeed selected) and the user can no longer double-click to expand a node, they must click the little '+' button.
Below is a generalization of my code, two methods from my custom class that extends JTree.
updateFilter gets called when the filter needs to be updated.
populateFilteredNode recursively populates the root node of my filtered model. For simplicity, filteredRoot is a class member variable (of type DefaultMutableTreeNode) and is the root of the filtered model. fullModel and filteredModel are of type DefaultTreeModel
public void updateFilter() {
// Get current expansion state
ArrayList<Integer> expansionState = getExpansionState();
DefaultMutableTreeNode fullModelRoot = fullModel.getRoot();
// Remove existing nodes in the filtered model
while(filteredRoot.getChildCount() > 0) {
filteredModel.removeNodeFromParent(filteredRoot.getFirstChild());
}
populateFilteredNode(fullModelRoot, filteredRoot);
// Repaint tree and restore expansion state
repaint();
setExpansionState(expansionState);
}
private void populateFilteredNode(DefaultMutableTreeNode fullNode, DefaultMutableTreeNode filteredNode) {
int index = 0;
for(int n = 0; n < fullNode.getChildCount(); n++) {
DefaultMutableTreeNode fullChildNode = fullNode.getChildAt(n);
// Show the item and its children if one of many cases is true
if(shouldShowItem(fullChildNode.getItem())) {
DefaultMutableTreeNode filteredChildNode = fullChildNode.clone();
filteredModel.insertNodeInto(filteredChildNode, filteredNode, index++);
populateFilteredNode(fullChildNode, filteredChildNode);
}
}
}
If anyone has a similar experience or knows why the selected node will not appear highlighted, please let me know. Or if there is a better way to accomplish filtering. Or if more code would help provide an answer.
I found something that works for my case, although it's quick and dirty and I don't necessarily understand why it works. This 12-year-old post on Code Ranch somehow got me headed in the right direction. I'm just posting it here in case anyone has a similar problem and it might be of help.
I save the selection path before making any changes to the table model, and then call this new function findNewSelectionPath after the changes were made. Below is a generalized version of the function (I use several custom classes so I did my best to make it look generically usable).
private TreePath findNewSelectionPath(TreePath oldSelectionPath) {
TreePath newSelectionPath = null;
if(oldSelectionPath != null) {
Object[] oldPathComponents = oldSelectionPath.getPath();
Object[] newPathComponents = new Object[oldPathComponents.length];
DefaultMutableTreeNode node = (DefaultMutableTreeNode) filteredModel.getRoot();
// Set the root
if(oldPathComponents[0].equals(node)) {
newPathComponents[0] = node;
}
// Set the rest of the path components
for(int n = 1; n < oldPathComponents.length; n++) {
for(int k = 0; k < node.getChildCount(); k++) {
if(oldPathComponents[n].equals(node.getChildAt(k))) {
newPathComponents[n] = node.getChildAt(k);
node = node.getChildAt(k);
break;
}
}
}
// Make sure that the last path component exists
if(newPathComponents[newPathComponents.length - 1] != null) {
newSelectionPath = new TreePath(newPathComponents);
}
}
return newSelectionPath;
}
Related
I have data like this (has other data like percentage, but is not important now) in a List that can vary:
1
1.1
1.1.1
1.1.2
1.2
2
2.1
2.2
How i easily work with the levels to build a proper JTree for any given levels?
Can be done with recursion?
What the best way?
Thank you so much.
Yes, it can easily be done using recursion. The idea is to check if there is already a node in the tree under which the new node can be fallen. For example, if the new node is "1.1.2", then we have to check if the node "1.1" exists in the tree. I wrote a very simple code and it is working, I am going yo cope here. If you don't understand something then just let me know, I will explain you. The function to check if the tree has the node of a particular string is given below.
public DefaultMutableTreeNode findparentnode(String s,DefaultMutableTreeNode root){
DefaultMutableTreeNode parent=null;
for (int i=0;i<root.getChildCount();i++) {
if(s.equalsIgnoreCase(((DefaultMutableTreeNode)root.getChildAt(i)).toString())){
parent = (DefaultMutableTreeNode)root.getChildAt(i);
break;
}
else
parent=findparentnode(s, (DefaultMutableTreeNode)root.getChildAt(i));
}
return parent;
}
Now, we will check every string in the list. We will skip the last part of the string, and will pass the remaining value to the function. To check the string, the code is given below
for(String s:list){
String[] substr=s.split("\\.");
String parent=substr[0];
for(int i=1;i<substr.length-1;i++){
parent=parent+ "." + substr[i];
}
DefaultMutableTreeNode node=null;
node=findparentnode(parent,root);
if(node==null)
root.add(new DefaultMutableTreeNode(s));
else
node.add(new DefaultMutableTreeNode(s));
}
I want to check whether a node with a certain ID is already in my graph or whether I have to create a new object. At the moment I am doing it with the following code:
// at this point I have the attributes for the node I need
String id = getIdOfNeededNode(); // The id is used to search for the node in the graph
// now I have to search for the node in the graph
Node node = new Node("dummy_id"); // This is the line I don't like;
// I would prefer not to have a dummy node
// but the compiler will then complain that the node might not be initialized
boolean alreadyCreated = false;
for(Node r : graph.getVertices()){ // search for the node with this id in the graph
if (r.getId().equals(portId)){
node = r;
alreadyCreated = true;
break;
}
}
if (!alreadyCreated) { // create a new object if the node was not found
node = new Resource(portId);
createdPortResources.add(port);
}
// In the remainder of the program, I am working with the node object which then is in the graph
The fact that I am creating a dummy node that is just a placeholder is really ugly. Please let me know how I can solve this problem in a more elegant way.
Well, you can just do this Node node = null;
But in general, just keep a map from the portIds to the nodes.
When you want to make that check, just consult that map.
It will be way easier and faster.
I need to implement a Trie (in Java) for a college project. The Trie should be able to add and remove Strings (for phase 1).
I have spent several hours each day (for the last few days) trying to figure out how to do this and FAILED miserably each time.
I require some help, the examples on the internet and my textbook (Data Structures and Algorithms in Java By Adam Drozdek) are not helping.
Information
Node classes I am working with:
class Node {
public boolean isLeaf;
}
class internalNode extends Node {
public String letters; //letter[0] = '$' always.
//See image -> if letter[1] = 'A' then children[1] refers to child node "AMMO"
//See image -> if letter[2] = 'B' then children[2] refers to internal node "#EU"
public TrieNode[] children = new TrieNode[2];
public TrieInternalNode(char ch)
{
letters = "#" + String.valueOf(ch);//letter[0] = '$' always.
isLeaf = false;
}
}
class leafNode extends Node
{
public String word;
public TrieLeafNode(String word)
{
this.word = new String(word);
isLeaf = true;
}
}
And here is the pseudo code for insert that I need to follow: (warning it is very vague)
trieInsert(String K)
{
i = 0;
p = the root;
while (not inserted)
{
if the end of word k is reached
set the end-of-word marker in p to true;
else if (p.ptrs[K[i]] == 0)
create a leaf containing K and put its address in p.ptrs[K[i]];
else if reference p.ptrs[K[i]] refers to a leaf
{
K_L = key in leaf p.ptrs[K[i]]
do
{
create a nonleaf and put its address in p.ptrs[K[i]];
p = the new nonleaf;
} while (K[i] == K_L[i++]);
}
create a leaf containing K and put its address in p.ptrs[K[--i]];
if the end of word k is reached
set the end-of-word marker in p to true;
else
create a leaf containing K_L and put its address in p.ptrs[K_L[i]];
else
p = p.ptrs[K[i++]];
}
}
I need to implement the following methods.
public boolean add(String word){...}//adds word to trie structure should return true if successful and false otherwise
public boolean remove(String word){...}//removes word from trie structure should return true if successful and false otherwise
I cant find pseudo code for remove, but if insert does not work delete wont help me.
Here is a image of how the Trie that I need to implement should look like.
I am aware that the Trie will still be inefficient if implemented like this, but at the moment I need not worry about this.
The book provides an implementation that is similar to what I need to do but doesn't use the end of word char ('$') and only stores the words without their prefixes in the child nodes http://mathcs.duq.edu/drozdek/DSinJava/SpellCheck.java
Constraints
I need to implement the trie in JAVA.
I may not import or use any of Java's built-in data structures. (ie. no Map, HashMap, ArrayList etc)
I may use Arrays, Java primitive Types and Java Strings.
The Trie must use a $ (dollar) symbol to indicate a end-of-word. (see the image below )
I may asume that now word containing the $symbol will be inserted.
I need to implement the Trie it in the same style as the book does.
Case of words doesn't matter ie. all words will be considered to be lowercase
The Trie should only store the end-of-word character and the characters applicable to a word and not the entire alphabet(like some implementations).
I do not expect anyone to do the implementation for me(unless they have one lying around :P) I just really need help.
First of all, I don't think you should make leaf nodes and internal nodes separate classes. I recommend making a universal node class with an isLeaf() method. This method would return true if a node has no children.
Here is some higher-level pseudocode for the functions you need to implement. For simplicity, I assume the existence of a method called getIndex() which returns the index corresponding to a character.
Insert(String str)
Node current = null
for each character in str
int index = getIndex(character)
if current.children[index] has not been initialized
initialize current.children[index] to be a new Node
current = current.children[index]
You can easily augment this pseudocode to fit your needs. For example, if you want to return false whenever insertion isn't successful:
Return false if the input string is null
Return false if the input string contains invalid characters
Now, here is some higher-level pseudocode for remove.
Remove(String str)
Node current = null
for each character in str
int index = getIndex(character)
current = current.children[index]
// At this point, we found the node we want to remove. However, we want to
// delete as many ancestor nodes as possible. We can delete an ancestor node
// if it is not need it any more. That is, we can delete an ancestor node
// if it has exactly one child.
Node ancestor = current
while ancestor is not null
if ancestor has 2 or more children
break out of loop
else if ancestor has less than 2 children
Node grandAncestor = ancestor.parent
if grandAncestor is not null
reinitialize grandAncestor.children // this has the effect of removing ancestor
ancestor = ancestor.parent
At a very high level, we follow the input string to the node we want to remove. After this, we traverse up the tree following parent pointers and delete every node with 1 child (since it is no longer needed). Once we reach a node with 2 children, we stop.
Like Insert, we can easily augment this pseudocode to return false whenever deletion isn't successful:
Return false if the input string is null
Return false if the input string contains invalid characters
Return false if the input string leads to a Node which doesn't exist
It is easiest to implement delete if your Node class has a parent field. However, it is possible to implement the method without parent points, but it is more difficult. You can see an example of the trickier implementation here.
I am creating an A Star search algorithm to solve a 8 puzzle board and I've got all the Board object classes listed within an ArrayList. My problem is I need to run methods within each of the Board objects to allow me to check if they have reached there goal, get board information and other functions similar to that.
Problem is I can't find a way after a few hours of internet searching that will solve this problem, I tried using an iterator to do the job which seems like the right direction but I couldn't get it to work but I don't have any experience with them.
Any help would be of great help.
public class Solve8Puzzle {
ArrayList startNode;
ArrayList nodes;
public Solve8Puzzle() {
startNode = new ArrayList();
nodes = new ArrayList();
}
public boolean checkGoalNodes() {
while( currently selected node has next ) {
run current node goal check
}
}
}
List<StartNode> startNode = new ArrayList<StartNode>();
.......................
for (StarNode node : starNodes) {
// do what you want with the node
}
Other possibility
for (Iterator<StarNode> it = starNodes.iterator(); it.hasNext(); ) {
StarNode node = it.next();
// do what you want with the node
}
I'm pretty new to wicket and I'm trying to create a simple wicket tree that holds information about mailinglists. This mailinglist is related to a certain letter.
MailingListDto 1
User 1
User 2
MailingListDto 2
User 3
User 4
If we are editing an existing letter, the mailinglists related to that letter are fetched into mailingListCollection and the corresponding nodes on the tree should be selected and expanded. For some reason I don't seem to get this workin.
The selected and expanded nodes do not show as selected nor expanded in the UI, but if I go through the selected nodes programmatically for example in onAfterRender() and log the selected and expanded values, the nodes are expanded and selected.
tree = new TreeTable("treeTable", treeModel, treeColumns) {
#Override
public void onBeforeRender() {
super.onBeforeRender();
if (!mailingListCollection.isEmpty()) {
for (MailingListDto mailingList : mailingListCollection) {
tree.getTreeState().expandNode(mailingList);
tree.getTreeState().selectNode(mailingList, true);
}
}
tree.updateTree();
}
#Override
protected void onAfterRender() {
super.onAfterRender();
if (LOG.isDebugEnabled()) {
LOG.debug("onAfterRender: " + tree.getTreeState().getSelectedNodes().size());
for (Object obj : tree.getTreeState().getSelectedNodes()) {
LOG.debug(tree.getTreeState().isNodeSelected(obj) + " " + tree.getTreeState().isNodeExpanded(obj));
}
}
}
};
tree.setRootLess(true);
tree.getTreeState().setAllowSelectMultiple(true);
add(tree);
To expand only the root node of your tree:
Object rootObj = myTree.getModelObject().getRoot();
myTree.getTreeState().expandNode(rootObj);
To expand also the first child of the root node add the following line to the previous ones:
myTree.getTreeState().expandNode(myTree.getModelObject().getChild(rootObj, 0));
Note that you have to expand all parent nodes of the "target" node otherwise on the screen the target node will be hidden because of a collapsed parent.
wicket 1.5.10
wicket 6
Look at source code. In class FooExpansion.java is a method expandAll().
FooExpansion.java is used in private class FooExpansionModel.java is used in AdvancedTreePage.java.
So a simple solution, when init your tree (in AdvancedTreePage.java), could be:
FooExansionModel model = new FooExpansionModell()
tree = createTree(provider, model);
((FooExpansion)model.getObject()).expandAll();