123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- /*
- * Copyright 2000-2016 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
- package com.vaadin.ui;
-
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.List;
- import java.util.Objects;
- import java.util.Optional;
- import java.util.stream.Stream;
-
- import org.jsoup.nodes.Attributes;
- import org.jsoup.nodes.Element;
- import org.jsoup.select.Elements;
-
- import com.vaadin.data.HierarchyData;
- import com.vaadin.data.ValueProvider;
- import com.vaadin.data.provider.DataProvider;
- import com.vaadin.data.provider.HierarchicalDataCommunicator;
- import com.vaadin.data.provider.HierarchicalDataProvider;
- import com.vaadin.data.provider.HierarchicalQuery;
- import com.vaadin.data.provider.InMemoryHierarchicalDataProvider;
- import com.vaadin.shared.ui.treegrid.NodeCollapseRpc;
- import com.vaadin.shared.ui.treegrid.TreeGridState;
- import com.vaadin.ui.declarative.DesignAttributeHandler;
- import com.vaadin.ui.declarative.DesignContext;
- import com.vaadin.ui.declarative.DesignFormatter;
- import com.vaadin.ui.renderers.AbstractRenderer;
- import com.vaadin.ui.renderers.Renderer;
-
-
- /**
- * A grid component for displaying hierarchical tabular data.
- *
- * @author Vaadin Ltd
- * @since 8.1
- *
- * @param <T>
- * the grid bean type
- */
- public class TreeGrid<T> extends Grid<T> {
-
- public TreeGrid() {
- super(new HierarchicalDataCommunicator<>());
-
- registerRpc(new NodeCollapseRpc() {
- @Override
- public void setNodeCollapsed(String rowKey, int rowIndex,
- boolean collapse) {
- if (collapse) {
- getDataCommunicator().doCollapse(rowKey, rowIndex);
- } else {
- getDataCommunicator().doExpand(rowKey, rowIndex);
- }
- }
- });
- }
-
- /**
- * Sets the data items of this component provided as a collection.
- * <p>
- * The provided items are wrapped into a
- * {@link InMemoryHierarchicalDataProvider} backed by a flat
- * {@link HierarchyData} structure. The data provider instance is used as a
- * parameter for the {@link #setDataProvider(DataProvider)} method. It means
- * that the items collection can be accessed later on via
- * {@link InMemoryHierarchicalDataProvider#getData()}:
- *
- * <pre>
- * <code>
- * TreeGrid<String> treeGrid = new TreeGrid<>();
- * treeGrid.setItems(Arrays.asList("a","b"));
- * ...
- *
- * HierarchyData<String> data = ((InMemoryHierarchicalDataProvider<String>)treeGrid.getDataProvider()).getData();
- * </code>
- * </pre>
- * <p>
- * The returned HierarchyData instance may be used as-is to add, remove or
- * modify items in the hierarchy. These modifications to the object are not
- * automatically reflected back to the TreeGrid. Items modified should be
- * refreshed with {@link HierarchicalDataProvider#refreshItem(Object)} and
- * when adding or removing items
- * {@link HierarchicalDataProvider#refreshAll()} should be called.
- *
- * @param items
- * the data items to display, not null
- */
- @Override
- public void setItems(Collection<T> items) {
- Objects.requireNonNull(items, "Given collection may not be null");
- setDataProvider(new InMemoryHierarchicalDataProvider<>(
- new HierarchyData<T>().addItems(null, items)));
- }
-
- /**
- * Sets the data items of this component provided as a stream.
- * <p>
- * The provided items are wrapped into a
- * {@link InMemoryHierarchicalDataProvider} backed by a flat
- * {@link HierarchyData} structure. The data provider instance is used as a
- * parameter for the {@link #setDataProvider(DataProvider)} method. It means
- * that the items collection can be accessed later on via
- * {@link InMemoryHierarchicalDataProvider#getData()}:
- *
- * <pre>
- * <code>
- * TreeGrid<String> treeGrid = new TreeGrid<>();
- * treeGrid.setItems(Stream.of("a","b"));
- * ...
- *
- * HierarchyData<String> data = ((InMemoryHierarchicalDataProvider<String>)treeGrid.getDataProvider()).getData();
- * </code>
- * </pre>
- * <p>
- * The returned HierarchyData instance may be used as-is to add, remove or
- * modify items in the hierarchy. These modifications to the object are not
- * automatically reflected back to the TreeGrid. Items modified should be
- * refreshed with {@link HierarchicalDataProvider#refreshItem(Object)} and
- * when adding or removing items
- * {@link HierarchicalDataProvider#refreshAll()} should be called.
- *
- * @param items
- * the data items to display, not null
- */
- @Override
- public void setItems(Stream<T> items) {
- Objects.requireNonNull(items, "Given stream may not be null");
- setDataProvider(new InMemoryHierarchicalDataProvider<>(
- new HierarchyData<T>().addItems(null, items)));
- }
-
- /**
- * Sets the data items of this listing.
- * <p>
- * The provided items are wrapped into a
- * {@link InMemoryHierarchicalDataProvider} backed by a flat
- * {@link HierarchyData} structure. The data provider instance is used as a
- * parameter for the {@link #setDataProvider(DataProvider)} method. It means
- * that the items collection can be accessed later on via
- * {@link InMemoryHierarchicalDataProvider#getData()}:
- *
- * <pre>
- * <code>
- * TreeGrid<String> treeGrid = new TreeGrid<>();
- * treeGrid.setItems("a","b");
- * ...
- *
- * HierarchyData<String> data = ((InMemoryHierarchicalDataProvider<String>)treeGrid.getDataProvider()).getData();
- * </code>
- * </pre>
- * <p>
- * The returned HierarchyData instance may be used as-is to add, remove or
- * modify items in the hierarchy. These modifications to the object are not
- * automatically reflected back to the TreeGrid. Items modified should be
- * refreshed with {@link HierarchicalDataProvider#refreshItem(Object)} and
- * when adding or removing items
- * {@link HierarchicalDataProvider#refreshAll()} should be called.
- *
- * @param items
- * the data items to display, not null
- */
- @Override
- public void setItems(@SuppressWarnings("unchecked") T... items) {
- Objects.requireNonNull(items, "Given items may not be null");
- setDataProvider(new InMemoryHierarchicalDataProvider<>(
- new HierarchyData<T>().addItems(null, items)));
- }
-
- @Override
- public void setDataProvider(DataProvider<T, ?> dataProvider) {
- if (!(dataProvider instanceof HierarchicalDataProvider)) {
- throw new IllegalArgumentException(
- "TreeGrid only accepts hierarchical data providers");
- }
- super.setDataProvider(dataProvider);
- }
-
- /**
- * Set the column that displays the hierarchy of this grid's data. By
- * default the hierarchy will be displayed in the first column.
- * <p>
- * Setting a hierarchy column by calling this method also sets the column to
- * be visible and not hidable.
- * <p>
- * <strong>Note:</strong> Changing the Renderer of the hierarchy column is
- * not supported.
- *
- * @see Column#setId(String)
- *
- * @param id
- * id of the column to use for displaying hierarchy
- */
- public void setHierarchyColumn(String id) {
- Objects.requireNonNull(id, "id may not be null");
- if (getColumn(id) == null) {
- throw new IllegalArgumentException("No column found for given id");
- }
- getColumn(id).setHidden(false);
- getColumn(id).setHidable(false);
- getState().hierarchyColumnId = getInternalIdForColumn(getColumn(id));
- }
-
- @Override
- protected TreeGridState getState() {
- return (TreeGridState) super.getState();
- }
-
- @Override
- protected TreeGridState getState(boolean markAsDirty) {
- return (TreeGridState) super.getState(markAsDirty);
- }
-
- @Override
- public HierarchicalDataCommunicator<T> getDataCommunicator() {
- return (HierarchicalDataCommunicator<T>) super.getDataCommunicator();
- }
-
- @Override
- public HierarchicalDataProvider<T, ?> getDataProvider() {
- if (!(super.getDataProvider() instanceof HierarchicalDataProvider)) {
- return null;
- }
- return (HierarchicalDataProvider<T, ?>) super.getDataProvider();
- }
-
- @Override
- protected void doReadDesign(Element design, DesignContext context) {
- super.doReadDesign(design, context);
- Attributes attrs = design.attributes();
- if (attrs.hasKey("hierarchy-column")) {
- setHierarchyColumn(DesignAttributeHandler
- .readAttribute("hierarchy-column", attrs, String.class));
- }
- }
-
- @Override
- protected void readData(Element body,
- List<DeclarativeValueProvider<T>> providers) {
- getSelectionModel().deselectAll();
- List<T> selectedItems = new ArrayList<>();
- HierarchyData<T> data = new HierarchyData<T>();
-
- for (Element row : body.children()) {
- T item = deserializeDeclarativeRepresentation(row.attr("item"));
- T parent = null;
- if (row.hasAttr("parent")) {
- parent = deserializeDeclarativeRepresentation(
- row.attr("parent"));
- }
- data.addItem(parent, item);
- if (row.hasAttr("selected")) {
- selectedItems.add(item);
- }
- Elements cells = row.children();
- int i = 0;
- for (Element cell : cells) {
- providers.get(i).addValue(item, cell.html());
- i++;
- }
- }
-
- setDataProvider(new InMemoryHierarchicalDataProvider<>(data));
- selectedItems.forEach(getSelectionModel()::select);
- }
-
- @Override
- protected void doWriteDesign(Element design, DesignContext designContext) {
- super.doWriteDesign(design, designContext);
- if (getColumnByInternalId(getState(false).hierarchyColumnId) != null) {
- String hierarchyColumn = getColumnByInternalId(
- getState(false).hierarchyColumnId).getId();
- DesignAttributeHandler.writeAttribute("hierarchy-column",
- design.attributes(), hierarchyColumn, null, String.class,
- designContext);
- }
- }
-
- @Override
- protected void writeData(Element body, DesignContext designContext) {
- getDataProvider().fetch(new HierarchicalQuery<>(null, null))
- .forEach(item -> writeRow(body, item, null, designContext));
- }
-
- private void writeRow(Element container, T item, T parent,
- DesignContext context) {
- Element tableRow = container.appendElement("tr");
- tableRow.attr("item", serializeDeclarativeRepresentation(item));
- if (parent != null) {
- tableRow.attr("parent", serializeDeclarativeRepresentation(parent));
- }
- if (getSelectionModel().isSelected(item)) {
- tableRow.attr("selected", "");
- }
- for (Column<T, ?> column : getColumns()) {
- Object value = column.getValueProvider().apply(item);
- tableRow.appendElement("td")
- .append(Optional.ofNullable(value).map(Object::toString)
- .map(DesignFormatter::encodeForTextNode)
- .orElse(""));
- }
- getDataProvider().fetch(new HierarchicalQuery<>(null, item))
- .forEach(childItem -> writeRow(container, childItem, item,
- context));
- }
-
- @Override
- protected <V> Column<T, V> createColumn(ValueProvider<T, V> valueProvider,
- AbstractRenderer<? super T, ? super V> renderer) {
- return new Column<T, V>(valueProvider, renderer) {
-
- @Override
- public com.vaadin.ui.Grid.Column<T, V> setRenderer(
- Renderer<? super V> renderer) {
- // Disallow changing renderer for the hierarchy column
- if (getInternalIdForColumn(this).equals(
- TreeGrid.this.getState(false).hierarchyColumnId)) {
- throw new IllegalStateException(
- "Changing the renderer of the hierarchy column is not allowed.");
- }
-
- return super.setRenderer(renderer);
- }
- };
- }
- }
|