I have the impression that this tree component (and for that matter the

I have the impression that this tree component (and for that matter the standard grid tree as well) support only one content type (class). This confused me; it is quite normal for a tree to show several different types of objects. Am I correct?

If so, I would like to refer to the STree component in my Sway project (aimed at Java Swing).

STree.of(cities)
        .childrenOf(City.class, City::getStreets)
        .childrenOf(Street.class, Street::getBuildings);

https://github.com/tbee/sway

It shows City, Street and Building classes on different levels. Each class has a renderer associated, so a node knows how to display itself.

I hope I'm wrong in that this Vaadin Tree component can only show one class.


Tree is extending TreeGrid, so it has basically the same data model. However since Tree has only one column, it is a bit more permissive. With TreeGrid you get easily in trouble with different properties of the objects, that are mutually incompatible.

With Tree you can use common abstract supertype as a workaround, something like this example:

@Route(value = "mixed-tree", layout = MainLayout.class)
public class MixedTree extends Div {

    public MixedTree() {
        setSizeFull();
        Library library = new Library();
        Tree<TreeObject> tree = new Tree<>(TreeObject::getName);
        tree.setItems(library.getAuthors(), library::getBooks);
        tree.setHeight(“500px”);
        tree.setHtmlProvider(item -> {
            String content = “”;
            if (item instanceof Author) {
                content = “Author: <b>” + item.getName() + “</b>”;
            } else if (item instanceof Book) {
                Book book = (Book) item;
                content = “Book: <i>” + book.getName() + “</i><br>” + "pages: "
                        + book.getPages();
            }
            return content;
        });
        add(tree);
    }

    public class Library {
        List<Book> books = new ArrayList<>();
        Random rand = new Random();

        public Library() {
            for (int i = 1; i < 5; i++) {
                Author author = new Author();
                author.setName("Author " + i);
                for (int j = 1; j < 5; j++) {
                    Book book = new Book(rand.nextInt(100, 200), author);
                    book.setName("Book " + j);
                    books.add(book);
                }
            }
        }

        public List<TreeObject> getAuthors() {
            return books.stream().map(book -> book.getAuthor()).distinct()
                    .collect(Collectors.toList());
        }

        public List<TreeObject> getBooks(TreeObject author) {
            return books.stream()
                    .filter(book -> book.getAuthor().equals(author))
                    .collect(Collectors.toList());
        }
    }

    public abstract class TreeObject {
        private String name;

        public String getName() {
            return name;
        }

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

    public class Author extends TreeObject {
    }

    public class Book extends TreeObject {
        private int pages;
        private Author author;

        public Book(int pages, Author author) {
            setPages(pages);
            setAuthor(author);
        }

        public int getPages() {
            return pages;
        }

        public void setPages(int pages) {
            this.pages = pages;
        }

        public Author getAuthor() {
            return author;
        }

        public void setAuthor(Author author) {
            this.author = author;
        }

    }
}

The Tatu's answer kind of has the solution, but only an example that scales to two levels. The trick for multiple levels is to use a common supertype (as suggested by Tatu, can be java.lang.Object as well) and then do instance of check for the "ValueProvider" that fetches the children for given node item. Here is a one with datatypes derived from your examples:


https://gist.github.com/mstahv/bba0a863069df5368282949593981c69


The same approach works also for the TreeGrid in the Vaadin core.


The API for this kind of cases might aid of some redesign. I was playing a bit with the Tree in the Viritin add-on (similar component in nature, but not using Vaadin Grid behind the scenes). I think there are probably smarter approaches we could come up with but to start the discussion I added an overloaded method for one can pass the children provider as an array (varargs): 


https://github.com/viritin/flow-viritin/blob/v24/src/test/java/org/vaadin/firitin/ClassHierarchyTreeAndTreeTableView.java#L122-L127