Thursday, 5 September 2013

Swing TreeTable Example with root using JXTreeTable

In my earlier post, I blogged about using different objects with a HAS-A relationship to be displayed in a tree table. However, I made the root node invisible and used a dummy object for the root.

In this post, I am going to use an object to represent the root and also display it. I am going to continue from where I left off by introducing an Organization object. This Organization object will contain a List<Department> object. Each Department object will in turn contain a List<Employee> object. This is same as in my earlier post. I think this reflects a real-world scenario of an organization with several departments and each department having many employees.

The Department and Employee classes remain the same. The Organization class looks as follows:

import java.util.List;

public class Organization {

    private String name;
    private List<Department> departmentList;

    public Organization(String name, List<Department> departmentList) {
        this.name = name;
        this.departmentList = departmentList;
    }

    public List<Department> getDepartmentList() {
        return departmentList;
    }

    public String getName() {
        return name;
    }
}

Changes to the tree-table model are along expected lines. Instead of using a dummy object for the row, I pass the Organization object itself to be considered as root, like (I have a different class name now):
public MyTreeTableModel(Organization organization) {
    super(organization);
}

The getValueAt() has an extra if condition to account for one more object in the hierarchy:
@Override
public Object getValueAt(Object node, int column) {
    if (node instanceof Organization) {
        if (1 == column) {
            return ((Organization) node).getName();
        }
    }
    if (node instanceof Department) {
        Department dept = (Department) node;
        switch (column) {
            case 0:
                return dept.getId();
            case 1:
                return dept.getName();
        }
    } else if (node instanceof Employee) {
        Employee emp = (Employee) node;
        switch (column) {
            case 0:
                return emp.getId();
            case 1:
                return emp.getName();
            case 2:
                return emp.getDoj();
            case 3:
                return emp.getPhoto();
        }
    }
    return null;
}
As the Organization has only one value to be displayed, I use the first column to do it.

Same way, the getChild() and getChildCount() methods have an extra condition:
@Override
public Object getChild(Object parent, int index) {
    if (parent instanceof Organization) {
        Organization org = (Organization) parent;
        return org.getDepartmentList().get(index);
    } else {
        Department dept = (Department) parent;
        return dept.getEmployeeList().get(index);
    }
}

@Override
public int getChildCount(Object parent) {
    if (parent instanceof Organization) {
        Organization org = (Organization) parent;
        return org.getDepartmentList().size();
    } else {
        Department dept = (Department) parent;
        return dept.getEmployeeList().size();
    }
}


Now, as I have 2 levels of hierarchy, the parent could be either Organization or Department. So, I do an instanceof check and return the corresponding child (or the child count).

Same goes for the getIndexOfChild() method. This method implementation is slightly more complex. Based on the parent, I do a cast and get the exact parent and child objects. Then, I call the appropriate method to get the list from which I can get the index of the child.

@Override
public int getIndexOfChild(Object parent, Object child) {
    if(parent instanceof Organization) {
        Organization org = (Organization) parent;
        Department dept = (Department) child;
        return org.getDepartmentList().indexOf(dept);
    }
    else {
        Department dept = (Department) parent;
        Employee emp = (Employee) child;
        return dept.getEmployeeList().indexOf(emp);
    }
}

While using this model in our GUI, we need to make the root visible. This can be done by calling treeTable.setRootVisible(true). And, we will be building the Organization object in addition to what we did earlier. The complete GUI code looks like:

public class TreeTableMainWithRoot extends JFrame {

    private JXTreeTable treeTable;
    
    public TreeTableMainWithRoot() {
        //sample doj
        final Date doj = Calendar.getInstance().getTime();
        List<Department> departmentList = new ArrayList<Department>();

        List<Employee> empList1 = new ArrayList<Employee>();
        empList1.add(new Employee(1, "Kiran", doj, "emp1.jpg"));
        empList1.add(new Employee(2, "Prabhu", doj, "emp2.jpg"));
        empList1.add(new Employee(3, "Murugavel", doj, "emp1.jpg"));
        departmentList.add(new Department(1, "Sales", empList1));

        List<Employee> empList2 = new ArrayList<Employee>();
        empList2.add(new Employee(4, "Deiveegan", doj, "emp2.jpg"));
        empList2.add(new Employee(5, "Saravanan", doj, "emp1.jpg"));
        departmentList.add(new Department(2, "Production", empList2));
        
        Organization organization = new Organization("ABC XYZ Corporation", departmentList);
        
        MyTreeTableModel myTreeTableModel = new MyTreeTableModel(organization);
        treeTable = new JXTreeTable(myTreeTableModel);
        treeTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        treeTable.setRootVisible(true);
        treeTable.getColumnModel().getColumn(3).setCellRenderer(new PhotoRenderer());
        treeTable.setRowHeight(50);

        add(new JScrollPane(treeTable));

        setTitle("JXTreeTable Example");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TreeTableMainWithRoot();
            }
        });
    }
}

When we run the code and expand all nodes, we get an output like:

The complete source code is available in the form of a Maven project in my github repo.

3 comments:

  1. Update: Modified code to make the editable functionality work correctly.
    Thanks Wenyuan for pointing this out!

    ReplyDelete
  2. Gracias amigo por el tutorial, y como seria si quisieramos poner mas de una organizacion.

    ReplyDelete
    Replies
    1. Hi Erick,
      I know only English and hence am unable to understand your comment.

      Delete