javaswingjtreedefaulttreemodel

Expanding specific JTree Node with FileTree as TreeModel


I am trying to build JTree based file browser and trying to navigate to particular path using setSelectionPath with DefaultMutableTreeNode.

I have taken the reference of the code from here

import java.io.File;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import static javax.management.Query.value;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

public class FileSysBrowser extends JFrame {

    public JTree fileTree;
    private FileSystemModel fileSystemModel;
    static String REPO = "repo/dinesh/trunk";
    static String[] REPO_PATH = REPO.split("/", -1);
    static int repoPathIdx = 0;
    private JTextArea fileDetailsTextArea = new JTextArea();

    public FileSysBrowser(String directory) {
        System.out.println(Arrays.toString(REPO_PATH));
        setTitle("FileSysBrowser");
        fileDetailsTextArea.setEditable(false);
        fileSystemModel = new FileSystemModel(new File(directory));
        fileTree = new JTree(fileSystemModel);
        fileTree.setEditable(true);
        fileTree.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent event) {
//                System.out.println(fileTree.getLastSelectedPathComponent());
//                File file = (File) fileTree.getLastSelectedPathComponent();
//                fileDetailsTextArea.setText(getFileDetails(file));
            }
        });
        getContentPane().add(new JScrollPane(fileTree));
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(640, 480);
        setVisible(true);
    }

    private DefaultMutableTreeNode buildNodeFromString(String path) {
        String[] s = path.split("/");
        File myFilePath = new File(path);

        DefaultMutableTreeNode node, lastNode = null, root = null;
        for (String str : s) {
            if (str.indexOf(":") != -1) {
                str = str.substring(0, str.indexOf(":"));
            }
            node = new DefaultMutableTreeNode(str);
            if (root == null) {
                root = node;
            }
            if (lastNode != null) {
                lastNode.add(node);
            }
            lastNode = node;
        }
        return root;
    }


    public static void main(String args[]) {

        FileSysBrowser fileSysBrowser = new FileSysBrowser("/");

        JTree jt = fileSysBrowser.fileTree;

//        jt.expandRow(21);
//        jt.expandRow(32);
//        TreePath pathForRow = jt.getPathForRow(4);
//        System.out.println(pathForRow);        
        DefaultMutableTreeNode node = fileSysBrowser.buildNodeFromString("repo/dinesh/trunk");

        TreePath pathForRow = new TreePath(node.getLastLeaf().getPath());
        System.out.println("pathForRow : " + pathForRow);
        jt.setSelectionPath(pathForRow);
    }
}

class FileSystemModel implements TreeModel {

    private File root;

    private final Vector listeners = new Vector();

    public FileSystemModel(File rootDirectory) {
        root = rootDirectory;
    }

    @Override
    public Object getRoot() {
        return root;
    }

    @Override
    public Object getChild(Object parent, int index) {
        File directory = (File) parent;
        String[] children = directory.list();
        Arrays.sort(children);
        String currentChild = children[index];
        return new TreeFile(directory, currentChild);
    }

    @Override
    public int getChildCount(Object parent) {        
        File file = (File) parent;
        if (file.isDirectory()) {
            String[] fileList = file.list();
            if (fileList != null) {
                return file.list().length;
            }
        }
        return 0;
    }

    @Override
    public boolean isLeaf(Object node) {
        if (node.getClass() == DefaultMutableTreeNode.class) {
            DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) node;
            System.out.println("chidcount ->" + dmtn.getChildCount());
            System.out.println(dmtn.getChildCount()==0);
            return dmtn.getChildCount() == 0;
        } else {
            File file = (File) node;
            return file.isFile();
        }
    }

    @Override
    public int getIndexOfChild(Object parent, Object child) {
        File directory = (File) parent;
        File file = (File) child;
        String[] children = directory.list();
        for (int i = 0; i < children.length; i++) {
            if (file.getName().equals(children[i])) {
                return i;
            }
        }
        return -1;

    }

    @Override
    public void valueForPathChanged(TreePath path, Object value) {
        File oldFile = (File) path.getLastPathComponent();
        String fileParentPath = oldFile.getParent();
        String newFileName = (String) value;
        File targetFile = new File(fileParentPath, newFileName);
        oldFile.renameTo(targetFile);
        File parent = new File(fileParentPath);
        int[] changedChildrenIndices = {getIndexOfChild(parent, targetFile)};
        Object[] changedChildren = {targetFile};
        fireTreeNodesChanged(path.getParentPath(), changedChildrenIndices, changedChildren);

    }

    private void fireTreeNodesChanged(TreePath parentPath, int[] indices, Object[] children) {
        TreeModelEvent event = new TreeModelEvent(this, parentPath, indices, children);
        Iterator iterator = listeners.iterator();
        TreeModelListener listener = null;
        while (iterator.hasNext()) {
            listener = (TreeModelListener) iterator.next();
            listener.treeNodesChanged(event);
        }
    }

    @Override
    public void addTreeModelListener(TreeModelListener listener) {
        listeners.add(listener);
    }

    @Override
    public void removeTreeModelListener(TreeModelListener listener) {
        listeners.remove(listener);
    }

    private class TreeFile extends File {

        public TreeFile(File parent, String child) {
            super(parent, child);
        }

        @Override
        public String toString() {
            return getName();
        }
    }
}

I have just included check in the isLeaf alone. As of now, it is not expanding the path.

With expandRow only, I am able to achieve the node expansion.

What should I do to make it work with setSelectionPath ?


Solution

  • After exploring about TreeModel, I found a solution. I have extended the DefaultMutableTreeNode as FileMutuableTreeNode and wherever DefaultMutableTreeNode is used with TreeModel, I have replaced it with FileMutuableTreeNode. Hope it is useful for those who are reading this in near future.

    The actual question is based on the below reference link.

    Reference : http://www.javaprogrammingforums.com/java-swing-tutorials/7944-how-use-jtree-create-file-system-viewer-tree.html

    FileSysBrowser.java

    //Reference : http://www.javaprogrammingforums.com/java-swing-tutorials/7944-how-use-jtree-create-file-system-viewer-tree.html
    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.File;
    import java.util.Arrays;
    import java.util.Enumeration;
    import java.util.Vector;
    import javax.swing.JButton;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    import javax.swing.JTree;
    import javax.swing.event.TreeModelListener;
    import javax.swing.event.TreeSelectionEvent;
    import javax.swing.event.TreeSelectionListener;
    import javax.swing.tree.TreeModel;
    import javax.swing.tree.TreePath;
    
    public class FileSysBrowser extends JFrame {
    
        public JTree fileTree;
        private FileSystemModel fileSystemModel;
    
        public FileSysBrowser(String directory) {
    //        System.out.println("REPO : " + Arrays.toString(REPO_PATH));
            setTitle("FileSysBrowser");
            fileSystemModel = new FileSystemModel(new FileMutableTreeNode(new File(directory)));
            fileTree = new JTree(fileSystemModel);
            fileTree.setEditable(false);
            JPanel pnlMain = new JPanel(new BorderLayout(5, 5));
            pnlMain.add(new JScrollPane(fileTree), BorderLayout.CENTER);
            final JButton btn = new JButton("click");
            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    new Thread() {
                        @Override
                        public void run() {
                            btn.setEnabled(false);
                            expandNodePath("/repo/dinesh/trunk/Design/");
                            btn.setEnabled(true);
                        }
                    }.start();
    
                }
            });
            pnlMain.add(btn, BorderLayout.PAGE_END);
            getContentPane().add(pnlMain);
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            setSize(640, 480);
            setVisible(true);
        }
    //Reference : https://stackoverflow.com/questions/8210630/how-to-search-a-particular-node-in-jtree-and-make-that-node-expanded 
    //Thanks to 'trashgod' for the answer    
        private FileMutableTreeNode findNode(FileMutableTreeNode root, String s) {
            @SuppressWarnings("unchecked")
            Enumeration<FileMutableTreeNode> e = root.depthFirstEnumeration();
            while (e.hasMoreElements()) {
                FileMutableTreeNode node = e.nextElement();
                if (node.toString().equals(s)) {
    //                return new TreePath(node.getPath());
                    return node;
                }
            }
            return null;
        }
    
        private void expandNodePath(String filePath) {
            if(filePath.startsWith("/")) {
                filePath = filePath.replaceFirst("/", "");
            }
            String[] files = filePath.split("/");
            //Initializing the parent node as 'root'
            FileMutableTreeNode parentNode = fileSystemModel.getRoot();
            for (String file : files) {
                FileMutableTreeNode childNode = findNode(parentNode, file);
                fileTree.expandPath(new TreePath(childNode.getPath()));
                // Then setting the current 'child' node as parent node 
                // for the next iteration
                parentNode = childNode;
            }
    
    //  The below code is what the expansion of above for loop code. 
    //  For ease of understanding, I have kept it for others. 
    //        FileMutableTreeNode repoNode = findNode(fileSystemModel.getRoot(), "repo");
    //        fileTree.expandPath(new TreePath(repoNode.getPath()));
    //        FileMutableTreeNode dineshNode = findNode(repoNode, "dinesh");
    //        fileTree.expandPath(new TreePath(dineshNode.getPath()));
    
        }
    
        public static void main(String args[]) {
    //        System.out.println(new File("/").getName());
    
            FileSysBrowser fileSysBrowser = new FileSysBrowser("/");
    
            JTree jt = fileSysBrowser.fileTree;
    
            jt.addTreeSelectionListener(new TreeSelectionListener() {
                @Override
                public void valueChanged(TreeSelectionEvent e) {
                    TreePath tp = e.getNewLeadSelectionPath();
                    if (tp != null) {
                        System.out.println(tp);
    //                    System.out.println(tp.getLastPathComponent());
                    }
                }
            });
        }
    }
    
    class FileSystemModel implements TreeModel {
    
    //    private File root;
        private FileMutableTreeNode root;
    
        private final Vector listeners = new Vector();
    
        public FileSystemModel(FileMutableTreeNode rootDirectory) {
            root = rootDirectory;
        }
    
        @Override
        public FileMutableTreeNode getRoot() {
            return root;
        }
    
        @Override
        public Object getChild(Object parent, int index) {
    
            FileMutableTreeNode parentNode = (FileMutableTreeNode) parent;
            File directory = parentNode.getFileNode();
            String[] children = directory.list();
            Arrays.sort(children);
            String currentChild = children[index];
            FileMutableTreeNode childNode = new FileMutableTreeNode(new File(directory, currentChild));
            if (parentNode.toString().equals("/")) {
                root.add(childNode);
            } else {
                parentNode.add(childNode);
            }
            return childNode;
        }
    
        @Override
        public int getChildCount(Object parent) {
            FileMutableTreeNode fileNode = (FileMutableTreeNode) parent;
            File file = fileNode.getFileNode();
            if (file.isDirectory()) {
                String[] fileList = file.list();
                if (fileList != null) {
                    return file.list().length;
                }
            }
            return 0;
        }
    
        @Override
        public boolean isLeaf(Object node) {
            FileMutableTreeNode fileNode = (FileMutableTreeNode) node;
            File file = fileNode.getFileNode();
            return file.isFile();
        }
    
        @Override
        public int getIndexOfChild(Object parent, Object child) {
            FileMutableTreeNode parentNode = (FileMutableTreeNode) parent;
            File directory = parentNode.getFileNode();
            FileMutableTreeNode childNode = (FileMutableTreeNode) child;
            File file = childNode.getFileNode();
            String[] children = directory.list();
            for (int i = 0; i < children.length; i++) {
                if (file.getName().equals(children[i])) {
                    return i;
                }
            }
            return -1;
    
        }
    
        @Override
        public void valueForPathChanged(TreePath path, Object value) {
            System.out.println("valueForPathChanged -> path : " + path + ", value : " + value);
        }
    
        @Override
        public void addTreeModelListener(TreeModelListener listener) {
            listeners.add(listener);
        }
    
        @Override
        public void removeTreeModelListener(TreeModelListener listener) {
            listeners.remove(listener);
        }
    
    }
    

    FileMutableTreeNode .java

    import java.io.File;
    import javax.swing.tree.DefaultMutableTreeNode;
    
    class FileMutableTreeNode extends DefaultMutableTreeNode {
    
        private File fileNode = null;
    
        public FileMutableTreeNode(File fileNode) {
            this.fileNode = fileNode;
        }
    
        public File getFileNode() {
            return fileNode;
        }
    
        @Override
        public String toString() {
            //For root, returning the name as '/'
            if (fileNode.getAbsolutePath().equals("/")) {
                return "/";
            } else {
                return fileNode.getName();
            }
        }
    }