I have a JTree implementation for directories in the file system. The problem I have is that when a directory is removed or renamed, the tree still shows the directory in it's parent. The parent tree seems not to be updated for any changes once a node has been expanded.
The model I have written does not cache (I have commented out what limited caching it did have) - it's like the JTree itself has cached the node.
The code (the model is a nested subclass):
public class FileSystemTree
extends JTree
{
// *****************************************************************************
// INSTANCE CREATE/DELETE
// *****************************************************************************
public FileSystemTree() {
this(new Model(null,null,null));
}
public FileSystemTree(String startPath) {
this(new Model(startPath,null,null));
}
public FileSystemTree(FileSelector inc, FileSelector exc) {
this(new Model(null,inc,exc));
}
public FileSystemTree(String startPath, FileSelector inc, FileSelector exc) {
this(new Model(startPath,inc,exc));
}
private FileSystemTree(Model model) {
super(model);
//setLargeModel(true);
setRootVisible(false);
setShowsRootHandles(true);
putClientProperty("JTree.lineStyle","Angled");
}
// *****************************************************************************
// INSTANCE METHODS - ACCESSORS
// *****************************************************************************
public Object getRoot() {
return getModel().getRoot();
}
// *****************************************************************************
// INSTANCE METHODS
// *****************************************************************************
public String convertValueToText(Object value,boolean selected,boolean expanded,boolean leaf,int row,boolean hasFocus) {
File fil=(File)value;
return (fil.getName().length()!=0 ? fil.getName() : fil.getPath());
}
// *****************************************************************************
// STATIC NESTED CLASSES - SUPPORTING MODEL
// *****************************************************************************
static class Model
extends Object
implements TreeModel, FilenameFilter
{
private File root; // tree root
//ivate Map cache; // caches child counts for directories
private File[] fsRoots; // copy of file system roots
private FileSelector include; // inclusion selector
private FileSelector exclude; // exclusion selector
private java.util.List listeners=new ArrayList();
public Model(String roo, FileSelector inc, FileSelector exc) {
super();
root=(roo==null ? DRIVES : new File(roo));
//cache=new HashMap();
fsRoots=(root==DRIVES ? rootList() : null);
include=inc;
exclude=exc;
}
// *****************************************************************************
// METHODS - MODEL
// *****************************************************************************
public Object getRoot() {
return root;
}
public Object getChild(Object parent, int index) {
File dir=(File)parent;
if(dir==DRIVES) {
File[] chl=fsRoots; //rootList();
return (index<chl.length ? chl[index] : null);
}
else {
String[] chl=dirList(dir);
return (index<chl.length ? new File(dir,chl[index]) : null);
}
}
public int getChildCount(Object parent) {
File dir=(File)parent;
//Integer cch=(Integer)cache.get(dir);
//if(cch!=null) {
// return cch.intValue();
// }
if(dir==DRIVES) {
return fsRoots.length; //rootList().length;
}
else if(dir.isDirectory()) {
return dirList(dir).length;
}
else {
return 0;
}
}
public boolean isLeaf(Object node) {
return((File)node).isFile();
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public int getIndexOfChild(Object parent, Object child) {
File dir=(File)parent;
File fse=(File)child;
if(dir==DRIVES) {
File[] ca=fsRoots; //rootList();
for(int xa=0; xa<ca.length; xa++) {
if(fse.equals(ca[xa])) { return xa; }
}
}
else {
String[] ca=dirList(dir);
for(int xa=0; xa<ca.length; ++xa) {
if(fse.getName().equals(ca[xa])) { return xa; }
}
}
return -1;
}
private File[] rootList() {
File[] lst=File.listRoots();
if(lst==null) { lst=new File[0]; }
//cache.put(DRIVES,new Integer(lst.length));
return lst;
}
private String[] dirList(File dir) {
String[] lst=dir.list(this);
if(lst==null) { lst=new String[0]; }
//cache.put(dir,new Integer(lst.length));
return lst;
}
// *****************************************************************************
// METHODS - FILENAME FILTER
// *****************************************************************************
public boolean accept(File dir, String nam) {
return ((include==null || include.accept(dir,nam)) && (exclude==null || !exclude.accept(dir,nam)));
}
// *****************************************************************************
// METHODS - LISTENER
// *****************************************************************************
public void addTreeModelListener(TreeModelListener listener) {
if(listener != null && !listeners.contains(listener)) {
listeners.add(listener);
}
}
public void removeTreeModelListener(TreeModelListener listener) {
if(listener != null) {
listeners.remove(listener);
}
}
public void fireTreeNodesChanged(TreeModelEvent evt) {
Iterator itr=listeners.iterator();
while(itr.hasNext()) {
TreeModelListener listener=(TreeModelListener)itr.next();
listener.treeNodesChanged(evt);
}
}
public void fireTreeNodesInserted(TreeModelEvent evt) {
Iterator itr=listeners.iterator();
while(itr.hasNext()) {
TreeModelListener listener=(TreeModelListener)itr.next();
listener.treeNodesInserted(evt);
}
}
public void fireTreeNodesRemoved(TreeModelEvent evt) {
Iterator itr=listeners.iterator();
while(itr.hasNext()) {
TreeModelListener listener=(TreeModelListener)itr.next();
listener.treeNodesRemoved(evt);
}
}
public void fireTreeStructureChanged(TreeModelEvent evt) {
Iterator itr=listeners.iterator();
while(itr.hasNext()) {
TreeModelListener listener=(TreeModelListener)itr.next();
listener.treeStructureChanged(evt);
}
}
} // END INNER CLASS
// *****************************************************************************
// STATIC PROPERTIES
// *****************************************************************************
static private final File DRIVES=new File("*DRIVES"); // marker for listing file system drives
} // END PUBLIC CLASS
The model is responsible for notifying the listeners of any modification in the tree structure. The tree will not refresh itself in absence of such a notification.
You could write some background thread that looks for changes in the filesystem and fire some model modification events if you detect that the directories have changed.
Without seeing the code for the Model, are you sure it is being called, such as the getChild method?
Related
In class Tree I got error message:
The method removeparent() is undefined for the type String.
I want to convert string "Grandchild3" to object which instance MyTreeNode class, then I can use removep("Grandchild3") call the method like this Grandchild3.removeparent().
How can I do this?
Here's class MyTreeNode:
public class MyTreeNode<T>{
private T data = null;
private List<MyTreeNode> children = new ArrayList<>();
private MyTreeNode parent = null;
public MyTreeNode(T data) {
this.data = data;
}
public void addChild(MyTreeNode child) {
child.setParent(this);
this.children.add(child);
}
public void addChild(T data) {
MyTreeNode<T> newChild = new MyTreeNode<>(data);
newChild.setParent(this);
children.add(newChild);
}
public void addChildren(List<MyTreeNode> children) {
for(MyTreeNode t : children) {
t.setParent(this);
}
this.children.addAll(children);
}
public List<MyTreeNode> getChildren() {
return children;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
private void setParent(MyTreeNode parent) {
this.parent = parent;
}
public MyTreeNode getParent() {
return parent;
}
public void removeparent() {
this.parent = null;
}
public void removeChild(MyTreeNode<T> child)
{
this.children.remove(child);
}
}
Here's class Tree:
public class Tree {
public static void main(String[] args) throws ClassNotFoundException {
// TODO Auto-generated method stub
MyTreeNode<String> root = new MyTreeNode<>("Root");
MyTreeNode<String> child1 = new MyTreeNode<>("Child1");
child1.addChild("Grandchild1");
child1.addChild("Grandchild2");
MyTreeNode<String> child2 = new MyTreeNode<>("Child2");
child2.addChild("Grandchild3");
root.addChild(child1);
root.addChild(child2);
root.addChild("Child3");
root.addChildren(Arrays.asList(
new MyTreeNode<>("Child4"),
new MyTreeNode<>("Child5"),
new MyTreeNode<>("Child6")
));
for(MyTreeNode<String> node : root.getChildren()) {
System.out.println(node.getData());
}
printTree(root, " ");
removep("Grandchild3"); //error message"The method removeparent() is undefined for the type String"
printTree(root, " ");
}
private static void printTree(MyTreeNode<String> node, String appender) {
System.out.println(appender+node.getData());
for (MyTreeNode each : node.getChildren()){
printTree(each, appender + appender);
}
}
public static void removep(MyTreeNode<String> node)
{
node.getParent().removeChild(node);
node.removeparent();
}
}
So basically what you want is to convert a string to a class object. Now there are probably several ways it could be done, but in the context used here it seems like you could simply pass the appropriate object instead of messing with strings.
So in your case, the most appropriate way to do so would be to create a method which receives a string name and the top node of your tree and iterates over the tree to find the node with the given name. When found the method returns the node.
Then you can use that method to get the node from the name and then call removep.
I have the following class;
public class RSSFeed implements Serializable {
private static final long serialVersionUID = 1L;
private int _itemcount = 0;
private List<RSSItem> _itemlist;
RSSFeed() {
_itemlist = new Vector<RSSItem>(0);
}
void addItem(RSSItem item) {
_itemlist.add(item);
_itemcount++;
}
public void removeItem(RSSItem item) {
_itemlist.remove(item);
_itemcount--;
}
public void shuffleList() {
Collections.shuffle(_itemlist);
}
public RSSItem getItem(int location) {
return _itemlist.get(location);
}
public int getItemCount() {
return _itemcount;
}
public void reverseList(){Collections.reverse(_itemlist);};
}
I can create an object of this class as follows;
public static RSSFeed feed;
Now, I want another object like public static RSSFeed **feed1**; where feed1 contains items 3 to 20 of the big object feed. How do I do this?
Thanks
If you need the top three from the List, you can iterate the List _itemlist and create a temporary List and return the temporary list.
public List<RSSItem> getTopThree() {
if(_itemlist != null){
List<RSSItem> temp = new ArrayList<RSSItem>();
for (RSSItem item : _itemlist) {
temp.add(item);
if(temp.size() == 3)
break;
}
return temp;
}
return null;
}
An optimisation would be to pre create and store the items as a field in the class RSSFeed.
According to #MouseEvents sugestion, you can also use sublist method of the ArrayList. See the code below.
public List<RSSItem> getTopThree() {
if (_itemlist != null) {
int end = _itemlist.size() < 3 ? _itemlist.size() : 3;
return _itemlist.subList(0, end);
}
return null;
}
I'm looking for a way to create a treeTable view in Nattable. I already have a NatTable implemented with filter, sorting, ...
But now I'm looking into a TreeTable like the TreeGridWithCheckBoxFieldsExample from the Nattable examples. The only requirement is that I do not change my datamodel for the tree.
I have two different objects Company and role. Every company does has all the roles. So in this situation I need a tree with all the companies as root object and all the roles beneath all the companies.
From the example it looks like I need to create a format class that implements the TreeList.Format but they are using the model to link the parent (I will not do this because it's a violation of the MVC principle.
Can someone get me on track to create a treetable view in NatTable?
After checking some example of Natable I got a working treeTable. But have only one problem left. The parent items are not correctly shown.
The treeFormat looks like:
public class TreeFormat implements TreeList.Format<PermissionViewModel> {
public TreeFormat() {
}
#Override
public Comparator getComparator(int depth) {
return new Comparator<PermissionViewModel>() {
#Override
public int compare(PermissionViewModel object1, PermissionViewModel object2) {
return object1.getModuleName().compareTo(object2.getModuleName());
}
};
}
#Override
public void getPath(List<PermissionViewModel> path, PermissionViewModel element) {
path.add(element);
PermissionViewModel parent = element.getParent();
while (parent != null) {
path.add(parent);
parent = parent.getParent();
}
Collections.reverse(path);
}
#Override
public boolean allowsChildren(PermissionViewModel element) {
return true;
}
The model that I use is a viewModel and is a one to one map to the normal model
public class PermissionViewModel implements Comparable {
private PermissionViewModel parent;
private ArrayList<PermissionViewModel> children = new ArrayList();
private Integer permissionId;
private String moduleName;
private String permissionName;
private boolean active;
private boolean on;
public PermissionViewModel(PermissionViewModel parent, Permission permission) {
this.parent = parent;
if (parent != null) {
parent.addChild(this);
}
if(parent == null && permission != null)
{
this.permissionId = 0;
this.moduleName = "";
this.permissionName = permission.getModuleName();
this.active = false;
}
else
{
this.permissionId = permission.getPermissionId();
this.moduleName = permission.getModuleName();
this.permissionName = permission.getPermissionName();
this.active = permission.isActive();
}
}
public PermissionViewModel getParent() {
return this.parent;
}
public void addChild(PermissionViewModel child) {
this.children.add(child);
}
public List getChildren() {
return this.children;
}
public PermissionViewModel getSelf() {
return this;
}
public boolean isOn() {
if (this.children.size() == 0) {
return this.on;
} else {
return getCheckBoxState() == CheckBoxStateEnum.CHECKED;
}
}
public void setOn(boolean on) {
if (this.children.size() == 0) {
this.on = on;
} else {
for (PermissionViewModel child : this.children) {
child.setOn(on);
}
}
}
public CheckBoxStateEnum getCheckBoxState() {
if (this.children.size() == 0) {
return this.on ? CheckBoxStateEnum.CHECKED
: CheckBoxStateEnum.UNCHECKED;
} else {
boolean atLeastOneChildChecked = false;
boolean atLeastOneChildUnchecked = false;
for (PermissionViewModel child : this.children) {
CheckBoxStateEnum childCheckBoxState = child.getCheckBoxState();
switch (childCheckBoxState) {
case CHECKED:
atLeastOneChildChecked = true;
break;
case SEMICHECKED:
return CheckBoxStateEnum.SEMICHECKED;
case UNCHECKED:
atLeastOneChildUnchecked = true;
break;
}
}
if (atLeastOneChildChecked) {
if (atLeastOneChildUnchecked) {
return CheckBoxStateEnum.SEMICHECKED;
} else {
return CheckBoxStateEnum.CHECKED;
}
} else {
return CheckBoxStateEnum.UNCHECKED;
}
}
}
#Override
public int compareTo(Object o) {
return 0;
}
public Integer getPermissionId() {
return permissionId;
}
public String getModuleName() {
return moduleName;
}
public String getPermissionName() {
return permissionName;
}
public boolean isActive() {
return active;
}
}
Putting data in the tree table will be done with following source:
ArrayList<PermissionViewModel> permissionViewModelsList = new ArrayList<>();
String previousModule = "";
PermissionViewModel currentParent = null;
for (Permission element : repo.getAllData()) {
if(!previousModule.equals(element.getModuleName()))
{
previousModule = element.getModuleName();
currentParent = new PermissionViewModel(null, element);
permissionViewModelsList.add(currentParent);
permissionViewModelsList.add(new PermissionViewModel(currentParent, element));
}
else
{
permissionViewModelsList.add(new PermissionViewModel(currentParent, element));
}
}
Collections.reverse(permissionViewModelsList);
permissionTable.setItems(permissionViewModelsList);
permissionTable.refresh(true);
But when I look at the table the root elements are viewed but the childeren of the root elements are wrong. I viewed the list of elements and there I can't find any issues. Can someone find the issue that I have?
I changed to order of the columns. I placed the "Module Name" before the "Name" column. And the parent module name has been placed in the module name column. This fixed my problem.
The issue in this case was that the comperator that I used is not working on mixed data in the same fields. Tnx to Dirk Fauth I found it.
I am implementing TreeModel interface and have implemented all methods except for the valueForPathChanged.
In my case, the values are not going to be programatically changed.
Why TreeModel interface contains this method? Is it used by JTree in any circumstances, or I am safe to leave it unimplemented?
Code for the reference, it works. I am just concerned whether valueForPathChanged is required by the JTree:
class ParamsTreeModel implements TreeModel {
private final TreeRoot root;
private final List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
ParamsTreeModel(TreeRoot root) {
this.root = root;
}
#Override
public void addTreeModelListener(TreeModelListener l) {
listeners.add(l);
}
#Override
public Object getChild(Object parent, int index) {
if(parent instanceof Param) return null;
if(structuredMap.containsKey(parent)) {
return structuredMap.get(parent).get(index);
}
// Root
return partNames.get(index);
}
#Override
public int getChildCount(Object parent) {
if(parent instanceof Param) return 0;
if(parent instanceof TreeRoot) return partNames.size();
return structuredMap.get(parent).size();
}
#Override
public int getIndexOfChild(Object parent, Object child) {
if(parent instanceof TreeRoot) return partNames.indexOf(child);
return structuredMap.get(parent).indexOf(child);
}
#Override
public Object getRoot() {
return root;
}
#Override
public boolean isLeaf(Object node) {
return (node instanceof Param);
}
#Override
public void removeTreeModelListener(TreeModelListener l) {
listeners.remove(l);
}
#Override
public void valueForPathChanged(TreePath path, Object newValue) {
// TODO Auto-generated method stub
}
}
While you are required to implement the valueForPathChanged() method, as defined in the TreeModel interface, you are free to leave it empty. FileTreeModel, cited here, is an example. The valueForPathChanged() method is typically used to support cell editing. As a concrete example, the implementation in DefaultTreeModel, seen here, "sets the user object of the TreeNode identified by path and posts a node changed [event]."
I am working on a eclipse rcp project where I need to create a treeviewer out of TreeStructured Java object. Currently I have hardcorded the Java objects to create the structure and that is working fine. I need to create a method which calculates all the Parents and children object and form the tree structure. It should be a recursive method. I wrote a recursive method to generate a
tree structured java object. But it does not work properly.
I dont know where exactly the problem. request you to help on this. Please find the code below.
private FileParent getInput() {
FileParent root = new FileParent("Root");
FileParent A = new FileParent("A");
FileParent a1 = new FileParent("A1");
FileObject a11 = new FileObject("A11");
a1.addChild(a11);
FileObject a2 = new FileObject("A2");
A.addChild(a1);
A.addChild(a2);
FileParent b = new FileParent("B"); FileObject b1 = new
FileObject("B1"); FileObject b2 = new FileObject("B2");
b.addChild(b1); b.addChild(b2);
root.addChild(A);
// root.addChild(b);
return root;
}
My recursive method which is to traverse through all the Parents and children objects
and creates the tree structured object.
FileParent root1 = new FileParent("Root");
public FileParent getChildren(FileParent root) {
if (!root.hasChildren()) {
return root;
}
if (root != null && root.hasChildren()) {
FileObject[] children = root.getChildren();// size two
for (FileObject fileObject : children) { // 2 times
// here children will be the folders
if (fileObject instanceof FileParent) {
FileParent folder = (FileParent) fileObject;
// root1.setParent(folder.getParent());
// if (root1.getParent() != null) {
root1.addChild(folder);
// }
getChildren((FileParent) folder);
} else {
System.out.println("FileName: " + fileObject.getName());
// root1.setParent(fileObject.getParent());
// if (root1.getParent() != null) {
root1.addChild(fileObject);
// }
}
}
}
return root1;
}
public class FileObject {
private String name;
private FileParent parent;
public FileObject(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setParent(FileParent parent) {
this.parent = parent;
}
public FileParent getParent() {
return parent;
}
public String toString() {
return getName();
}
}
public class FileParent extends FileObject {
/**
*
*/
private List<FileObject> children;
public FileParent(String name) {
super(name);
children = new ArrayList<FileObject>();
}
public void addChild(FileObject child) {
// if (children.contains(child)) {
children.add(child);
child.setParent(this);
// }
}
public void removeChild(FileObject child) {
children.remove(child);
child.setParent(null);
}
public FileObject[] getChildren() {
return (FileObject[]) children.toArray(new FileObject[children.size()]);
}
public boolean hasChildren() {
return children.size() > 0;
}
}
In Swing there is interface TreeModel with default implementation Supposed to be used in JTree and generally not written the best way (like no generics), but can do the job.
There is one method in treeviewer.setInput(Object obj).If I pass getInput() as parameter It will work fine. But if I pass getChildren() which is my recursive method it does not work.
Do you mean you are calling treeviewer.setInput(foo.getChildren())? This is definitely wrong. Read this article: http://www.vogella.com/articles/EclipseJFaceTree/article.html.
Basically, you need to 1) implement ITreeContentProvider to provide the tree structure; 2) implement ILabelProvider to provide text and images; 3) call
treeViewer.setContentProvider(yourContentProvider);
treeViewer.setLabelProvider(yourLabelProvider);
treeviewer.setInput(new Object()); // or anything for which your content provider returns correct roots