0


I have a Factory class which contains a list of employees. I want to use a TreeTableView to display the Factory data. It is pretty forward to display the name and the size of a Factory, but i don't know how to display the employees names!

public class Factory {
       private String name;
       private double size;
       private List<Employee> employees;

       public Factory(name, size){this.name=name; this.size=size} 

       // Getters & setters

    }

I want to have the following output:

enter image description here

With the possibilty to fold the factory.

2
  • By "fold" do you mean in a drop down? Commented Sep 20, 2016 at 17:18
  • I don ' t know if it's possible in that exact way, you can do something similar seeing the factory name and the size repeated for each record Commented Sep 20, 2016 at 18:40

1 Answer 1

4

In a TreeView or TreeTableView all nodes in the tree have to be of the same type. This makes the kind of design you want (which is very natural) something of a pain. Basically, you have to make the type of the TreeView or TreeTableView the most specific superclass of all the types of rows you want in the tree: i.e. in this case the type of the TreeTableView needs to be a superclass of both Employee and Factory. Then the cell value factories on the columns would have to type test the row objects to determine what value to return.

It would be unusual to have an object model in which these were related by inheritance other than both being subclasses of Object, so you probably need a TreeTableView<Object> here.

So roughly speaking (if you are using plain old JavaBean style, instead of the recommended JavaFX properties), you would define something like

TreeTableView<Object> treeTable = new TreeTableView<>();
treeTable.setShowRoot(false);

TreeTableColumn<Object, String> nameColumn = new TreeTableColumn<>("Name");
nameColumn.setCellValueFactory(cellData -> {
    TreeItem<Object> rowItem = cellData.getValue();
    if (rowItem != null && (rowItem.getValue() instanceof Factory)) {
        Factory f = (Factory) rowItem.getValue() ;
        return new SimpleStringProperty(f.getName());
    } else {
        return new SimpleStringProperty("");
    }
});

TreeTableColumn<Object, Number> sizeColumn = new TreeTableColumn<>("Size");
sizeColumn.setCellValueFactory(cellData -> {
    TreeItem<Object> rowItem = cellData.getValue();
    if (rowItem != null && (rowItem.getValue() instanceof Factory)) {
        Factory f = (Factory) rowItem.getValue() ;
        return new SimpleObjectProperty<Number>(Double.valueOf(f.getSize()));
    } else {
        return new SimpleObjectProperty<Number>(null);
    }
});

TreeTableColumn<Object, String> employeeColumn = new TreeTableColumn<>("Employee");
employeeColumn.setCellValueFactory(cellData -> {
    TreeItem<Object> rowItem = cellData.getValue();
    if (rowItem != null && (rowItem.getValue() instanceof Employee)) {
        Employee emp = (Employee) rowItem.getValue() ;
        return new SimpleStringProperty(emp.getName());
    } else {
        return new SimpleStringProperty("");
    }
});

treeTable.getColumns().addAll(nameColumn, sizeColumn, employeeColumn);

and of course you populate it with

// fully initialized list of factories, with employee lists initialized:
List<Factory> factories = ... ;

TreeItem<Object> root = new TreeItem<>(null);
for (Factory factory : factories) {
    TreeItem<Object> factoryItem = new TreeItem<>(factory);
    root.getChildren().add(factoryItem);
    for (Employee emp : factory.getEmployees()) {
        TreeItem<Object> employeeItem = new TreeItem<>(emp);
        factoryItem.getChildren().add(employeeItem);
    }
}
treeTable.setRoot(root);

Here's a simple SSCCE using this:

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;

public class TreeTableExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        TreeTableView<Object> treeTable = new TreeTableView<>();
        treeTable.setShowRoot(false);

        TreeTableColumn<Object, String> nameColumn = new TreeTableColumn<>("Name");
        nameColumn.setCellValueFactory(cellData -> {
            TreeItem<Object> rowItem = cellData.getValue();
            if (rowItem != null && (rowItem.getValue() instanceof Factory)) {
                Factory f = (Factory) rowItem.getValue() ;
                return new SimpleStringProperty(f.getName());
            } else {
                return new SimpleStringProperty("");
            }
        });

        TreeTableColumn<Object, Number> sizeColumn = new TreeTableColumn<>("Size");
        sizeColumn.setCellValueFactory(cellData -> {
            TreeItem<Object> rowItem = cellData.getValue();
            if (rowItem != null && (rowItem.getValue() instanceof Factory)) {
                Factory f = (Factory) rowItem.getValue() ;
                return new SimpleObjectProperty<Number>(Double.valueOf(f.getSize()));
            } else {
                return new SimpleObjectProperty<Number>(null);
            }
        });

        TreeTableColumn<Object, String> employeeColumn = new TreeTableColumn<>("Employee");
        employeeColumn.setCellValueFactory(cellData -> {
            TreeItem<Object> rowItem = cellData.getValue();
            if (rowItem != null && (rowItem.getValue() instanceof Employee)) {
                Employee emp = (Employee) rowItem.getValue() ;
                return new SimpleStringProperty(emp.getName());
            } else {
                return new SimpleStringProperty("");
            }
        });

        treeTable.getColumns().addAll(nameColumn, sizeColumn, employeeColumn);

        List<Factory> factories = createData();
        TreeItem<Object> root = new TreeItem<>(null);
        for (Factory factory : factories) {
            TreeItem<Object> factoryItem = new TreeItem<>(factory);
            root.getChildren().add(factoryItem);
            for (Employee emp : factory.getEmployees()) {
                TreeItem<Object> employeeItem = new TreeItem<>(emp);
                factoryItem.getChildren().add(employeeItem);
            }
        }
        treeTable.setRoot(root);

        Scene scene = new Scene(treeTable, 800, 800);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private List<Factory> createData() {
        String[][] empNames = {
                {"John", "Jane", "Mary"},
                {"Susan", "Mike"},
                {"Alex", "Francois", "Joanne"}
        };
        List<Factory> factories = new ArrayList<>();
        for (String[] emps : empNames) {
            int count = factories.size()+1 ;
            Factory f = new Factory("Factory "+ count, count*10);
            for (String empName : emps) {
                f.getEmployees().add(new Employee(empName));
            }
            factories.add(f);
        }
        return factories ;
    }

    public static class Employee {
        private String name ;

        public Employee(String name) {
            this.name = name ;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }


    }

    public class Factory {
        private String name ;
        private double size ;
        private List<Employee> employees ;

        public Factory(String name, double size) {
            this.name = name ;
            this.size = size ;
            this.employees = new ArrayList<>();
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public double getSize() {
            return size;
        }

        public void setSize(double size) {
            this.size = size;
        }

        public List<Employee> getEmployees() {
            return employees;
        }


    }

    public static void main(String[] args) {
        launch(args);
    }
}

enter image description here


Another approach, which I think is a bit artificial, is to create a class representing the row in the table view, and then to make Factory and Employee subclasses of it:

public abstract class EmploymentEntity {

    public String getName() {
        return null ;
    }

    public Double getSize() {
        return null ;
    }

    public String getEmployeeName {
        return null ;
    }
}

then

public class Employee extends EmploymentEntity {
    private String name ;

    public Employee(String name) {
        this.name = name ;
    }

    @Override
    public String getEmployeeName() {
        return name ;
    }

    public void setEmployeeName(String name) {
        this.name = name ; 
    }
}

and

public class Factory extends EmploymentEntity {

    private String name ;
    private double size ;
    private List<Employee> employees ;

    public Factory(String name, double size) {
        this.name = name ;
        this.size = size ;
        this.employees = new ArrayList<>();
    }

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

    public void setName(String name) {
        this.name = name ;
    }

    @Override
    public Double getSize() {
        return size ;
    }

    public void setSize(double size) {
        this.size = size ;
    }

    public List<Employee> getEmployees() {
        return employees ;
    }
}

This object model is really unnatural (to me, anyway), but it does make the table a little easier:

TreeTableView<EmploymentEntity> treeTable = new TreeTableView<>();
TreeTableColumn<EmploymentEntity, String> nameColumn = new TreeTableColumn<>("Name");
nameColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getValue().getName()));
TreeTableColumn<EmploymentEntity, Number> sizeColumn = new TreeTableColumn<>("Size");
sizeColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<Number>(cellData.getValue().getValue().getSize()));
TreeTableColumn<EmploymentEntity, String> employeeColumn = new TreeTableColumn<>("Employee");
employeeColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getValue().getEmployeeName()));
// etc...
Sign up to request clarification or add additional context in comments.

4 Comments

A modification of this using an object to represent the table row which contains the Employee and Factory would achieve the same simplification of the table. This wouldn't require altering the data model to extend a common ancestor, and removes the need for casting etc required by the Object version.
@Geoff That looks like a nice idea. I don't see a clean way to implement the cell value factories though; it avoids type-testing but the logic would still be pretty messy (unless I'm overlooking something, which is certainly possible).
It would be essentially the same as your second solution I thought, but instead of having domain objects descending from the common class, a wrapper class would do so, with one implementation for each domain object. Cell value factories would be identical to yours. It would mean more classes which would be a downside, so I guess it depends on which downside is preferred?
Ah, so the get methods in the wrapper would return null or delegate to the wrapped instance, something like that? That might work well - at least for a two level hierarchy like this (the first solution is probably the lesser of all evils once you have more than two levels in the hierarchy).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.