I have this project where my program fetches files from a operating system disk image and stores their details in a database and another functionality fetches the files from the database and stores them in a List (my own object type) and is supposed to create a JTree after fetching their paths (also stored in the database) and splitting the path strings based on slashes so that files inside various folders are leaf nodes and folders are parents. The fetching of the files works perfectly but I think something is wrong with creating the JTree since some folders are getting displayed as leaf nodes in the JTree. Here's a screenshot:
Let me tell you what my code looks like:
public class ActiveCase {
private void populateList(){
//a function that fetches a list of files from a database and stores it in a list
List<AbstractFile> l = *a query function that stores the files in the list*;
listFiles(l);
}
public void listFiles(List<AbstractFile> fl){
ArrayList<TreePath> te = new ArrayList<TreePath>();
try {
for(AbstractFile file : fl){
//don't worry about the isDir() and getName() functions they work
if(!file.isDir() && !file.getName().startsWith(".")){
te.add(new TreePath(("File list"+formatPath(file.getUniquePath())).split("/")));
}
}
} catch (TskCoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
TreeModel tmClean = new TreePathsTreeModel("File list", te);
cleanPanel.add(new JTree(tmClean));
}
private static String formatPath(String uniquePath){
String[] pathSegments = uniquePath.split("/");
int index = 3;
StringBuilder strbuf = new StringBuilder();
for (; index < pathSegments.length; ++index) {
if (!pathSegments[index].isEmpty()) {
strbuf.append("/").append(pathSegments[index]);
}
}
return strbuf.toString();
}
}
And I've got a TreePathsTreeModel class that implements TreeModel:
public class TreePathsTreeModel implements TreeModel {
private final ArrayList<TreePath> paths;
private final String root;
public TreePathsTreeModel(String root, ArrayList<TreePath> te) {
this.root = root;
this.paths = te;
//a little test print statement to see if the TreePath list is in the right format
for(TreePath t : paths){
System.out.println(t.toString());
}
}
@Override
public Object getRoot() {
return this.root;
}
@Override
public Object getChild(Object parent, int index) {
try {
return getChildren(parent).get(index);
} catch (IndexOutOfBoundsException ex) {
return null;
}
}
@Override
public int getChildCount(Object parent) {
return getChildren(parent).size();
}
@Override
public boolean isLeaf(Object node) {
for (int i = 0; i < paths.size(); i++) {
TreePath treePath = paths.get(i);
if (treePath.getLastPathComponent().equals(node))
return true;
}
return false;
}
// This method is only required for editable trees, so it is not
// implemented here.
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
//throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public int getIndexOfChild(Object parent, Object child) {
return getChildren(parent).indexOf(child);
}
// This TreeModel never fires any events (since it is not editable)
// so event listener registration methods are left unimplemented
@Override
public void addTreeModelListener(TreeModelListener l) {
//throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void removeTreeModelListener(TreeModelListener l) {
//throw new UnsupportedOperationException("Not supported yet.");
}
//search all paths in list for given object
//return every item one level further than it
private ArrayList<String> getChildren(Object parent) {
ArrayList<String> children = new ArrayList<String>();
for (int i = 0; i < this.paths.size(); i++) {
ArrayList<Object> pathObjects = new ArrayList<Object>( Arrays.asList(this.paths.get(i).getPath()) );
for (Iterator<Object> it = pathObjects.iterator(); it.hasNext();) {
Object parentCandidate = it.next();
if (parentCandidate.equals(parent)) {
Iterator<Object> checker = it;
try {
String child = new DefaultMutableTreeNode( checker.next() ).toString();
if ( !children.contains(child) )
children.add (child);
} catch (NoSuchElementException ex) {
}
}
}
}
return children;
}
}
As far as I can tell from the print statement in the TreePathsTreeModel constructor the list is in the right format. I'll paste a sample of the output below:
...
[File list, dev, tty0]
[File list, dev, tty1]
[File list, dev, tty2]
[File list, dev, tty3]
[File list, dev, tty4]
[File list, dev, tty5]
[File list, dev, tty6]
[File list, dev, tty7]
[File list, dev, tty8]
[File list, dev, tty9]
[File list, dev, urandom]
[File list, dev, zero]
[File list, home, mainvm, examples.desktop]
[File list, home, mainvm, .cache, wallpaper, 0_5_1920_950_792beab7550410d531e55f95b449f135]
[File list, home, mainvm, .cache, upstart, unity7.log.2.gz]
[File list, home, mainvm, .cache, upstart, ssh-agent.log.2.gz]
[File list, home, mainvm, .cache, upstart, gnome-keyring-ssh.log.1.gz]
[File list, home, mainvm, .cache, upstart, dbus.log]
[File list, home, mainvm, .cache, upstart, unity7.log]
[File list, home, mainvm, .cache, upstart, hud.log]
[File list, home, mainvm, .cache, upstart, upstart-event-bridge.log.2.gz]
[File list, home, mainvm, .cache, upstart, unity7.log.1.gz]
[File list, home, mainvm, .cache, upstart, unity-settings-daemon.log.2.gz]
[File list, home, mainvm, .cache, upstart, unity-panel-service.log.2.gz]
[File list, home, mainvm, .cache, upstart, hud.log.1.gz]
[File list, home, mainvm, .cache, upstart, window-stack-bridge.log.2.gz]
[File list, home, mainvm, .cache, upstart, gpg-agent.log.2.gz]
[File list, home, mainvm, .cache, upstart, ssh-agent.log.1.gz]
[File list, home, mainvm, .cache, upstart, window-stack-bridge.log.1.gz]
[File list, home, mainvm, .cache, upstart, unity-panel-service.log]
[File list, home, mainvm, .cache, upstart, unity-settings-daemon.log.1.gz]
[File list, home, mainvm, .cache, upstart, unity-panel-service.log.1.gz]
[File list, home, mainvm, .cache, upstart, dbus.log.2.gz]
[File list, home, mainvm, .cache, upstart, dbus.log.1.gz]
[File list, home, mainvm, .cache, upstart, gnome-keyring-ssh.log.2.gz]
[File list, home, mainvm, .cache, upstart, gpg-agent.log.1.gz]
[File list, home, mainvm, .cache, upstart, indicator-sound.log.2.gz]
[File list, home, mainvm, .cache, upstart, indicator-sound.log.1.gz]
[File list, home, mainvm, .cache, upstart, upstart-event-bridge.log.1.gz]
[File list, home, mainvm, .cache, ibus, bus, registry.F0VAPY]
...
[File list, home, mainvm, .local, share, recently-used.xbel.49E0TY]
[File list, home, mainvm, .local, share, recently-used.xbel]
[File list, home, mainvm, .gnupg, gnupg_spawn_agent_sentinel.lock]
[File list, home, mainvm, .gnupg, S.gpg-agent]
[File list, lib, apparmor, functions]
[File list, lib, apparmor, profile-load]
[File list, lib, brltty, brltty.sh]
[File list, lib, brltty, libbrlttybal.so]
[File list, lib, brltty, libbrlttybat.so]
[File list, lib, brltty, libbrlttybba.so]
[File list, lib, brltty, libbrlttybbc.so]
[File list, lib, brltty, libbrlttybbd.so]
[File list, lib, brltty, libbrlttybbg.so]
[File list, lib, brltty, libbrlttybbl.so]
[File list, lib, brltty, libbrlttybbm.so]
[File list, lib, brltty, libbrlttybbn.so]
[File list, lib, brltty, libbrlttybcb.so]
[File list, lib, brltty, libbrlttybce.so]
[File list, lib, brltty, libbrlttybec.so]
[File list, lib, brltty, libbrlttybeu.so]
...
Maybe there's something right under my nose that I'm missing. It might as well be a stupid mistake. Any ideas? This is my final year project in college, please help!
The bug is most probably in the isLeaf(Object node)
method of your TreeModel
.
Look at the javadoc of TreeModel.isLeaf(Object node)
:
Returns
true
ifnode
is a leaf. It is possible for this method to returnfalse
even ifnode
has no children. A directory in a filesystem, for example, may contain no files; the node representing the directory is not a leaf, but it also has no children.
So, in case of a directory (even an empty directory) you need to return false
instead of true
.