summaryrefslogtreecommitdiffstats
path: root/server/src
diff options
context:
space:
mode:
authorTeemu Suo-Anttila <teemusa@vaadin.com>2016-08-22 16:39:50 +0300
committerVaadin Code Review <review@vaadin.com>2016-08-24 12:11:31 +0000
commit87e0a08b6059add166b46b8b4379791a40ff0ede (patch)
tree7dbe56876a7befbbe9985343b165ee91f65ed4c4 /server/src
parentfa2a40b38e8f06632f9b89c9acea1ed0d40c3410 (diff)
downloadvaadin-framework-87e0a08b6059add166b46b8b4379791a40ff0ede.tar.gz
vaadin-framework-87e0a08b6059add166b46b8b4379791a40ff0ede.zip
Add a typed version of the Grid component
Change-Id: I00cbe80ac72787de0c4cc88e1c223badc2c4ae89
Diffstat (limited to 'server/src')
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractListing.java28
-rw-r--r--server/src/main/java/com/vaadin/ui/Grid.java408
2 files changed, 434 insertions, 2 deletions
diff --git a/server/src/main/java/com/vaadin/ui/AbstractListing.java b/server/src/main/java/com/vaadin/ui/AbstractListing.java
index 64d55926d5..05f31d7b81 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractListing.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractListing.java
@@ -21,7 +21,7 @@ import com.vaadin.server.data.DataSource;
import com.vaadin.server.data.TypedDataGenerator;
/**
- * Base class for Listing components. Provides common handling for
+ * Base class for {@link Listing} components. Provides common handling for
* {@link DataCommunicator} and {@link TypedDataGenerator}s.
*
* @param <T>
@@ -31,7 +31,31 @@ public abstract class AbstractListing<T> extends AbstractComponent
implements Listing<T> {
/* DataCommunicator for this Listing component */
- private final DataCommunicator<T> dataCommunicator = new DataCommunicator<>();
+ private final DataCommunicator<T> dataCommunicator;
+
+ /**
+ * Constructs an {@link AbstractListing}, extending it with a
+ * {@link DataCommunicator}.
+ */
+ protected AbstractListing() {
+ this(new DataCommunicator<>());
+ }
+
+ /**
+ * Constructs an {@link AbstractListing}, extending it with given
+ * {@link DataCommunicator}.
+ * <p>
+ * <strong>Note:</strong> This method is for creating an
+ * {@link AbstractListing} with a custom {@link DataCommunicator}. In the
+ * common case {@link AbstractListing#AbstractListing()} should be used.
+ *
+ * @param dataCommunicator
+ * a customized data communicator instance
+ */
+ protected AbstractListing(DataCommunicator<T> dataCommunicator) {
+ this.dataCommunicator = dataCommunicator;
+ addExtension(dataCommunicator);
+ }
@Override
public void setDataSource(DataSource<T> dataSource) {
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java
new file mode 100644
index 0000000000..553d5e0761
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/Grid.java
@@ -0,0 +1,408 @@
+/*
+ * 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.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import com.vaadin.server.AbstractExtension;
+import com.vaadin.server.KeyMapper;
+import com.vaadin.server.data.DataSource;
+import com.vaadin.server.data.SortOrder;
+import com.vaadin.server.data.TypedDataGenerator;
+import com.vaadin.shared.MouseEventDetails;
+import com.vaadin.shared.data.DataCommunicatorConstants;
+import com.vaadin.shared.data.sort.SortDirection;
+import com.vaadin.shared.ui.grid.ColumnState;
+import com.vaadin.shared.ui.grid.GridConstants.Section;
+import com.vaadin.shared.ui.grid.GridServerRpc;
+
+import elemental.json.Json;
+import elemental.json.JsonObject;
+
+/**
+ * A grid component for displaying tabular data.
+ *
+ * @author Vaadin Ltd
+ * @since
+ *
+ * @param <T>
+ * the grid bean type
+ */
+public class Grid<T> extends AbstractListing<T> {
+
+ private final class GridServerRpcImpl implements GridServerRpc {
+ @Override
+ public void sort(String[] columnIds, SortDirection[] directions,
+ boolean isUserOriginated) {
+ assert columnIds.length == directions.length : "Column and sort direction counts don't match.";
+ sortOrder.clear();
+ if (columnIds.length == 0) {
+ // Grid is not sorted anymore.
+ getDataCommunicator()
+ .setBackEndSorting(Collections.emptyList());
+ getDataCommunicator().setInMemorySorting(null);
+ return;
+ }
+
+ for (int i = 0; i < columnIds.length; ++i) {
+ Column<T, ?> column = columnKeys.get(columnIds[i]);
+ sortOrder.add(new SortOrder<>(column, directions[i]));
+ }
+
+ // Set sort orders
+ // In-memory comparator
+ Comparator<T> comparator = sortOrder.stream()
+ .map(order -> order.getSorted()
+ .getComparator(order.getDirection()))
+ .reduce((x, y) -> 0, Comparator::thenComparing);
+ getDataCommunicator().setInMemorySorting(comparator);
+
+ // Back-end sort properties
+ List<SortOrder<String>> sortProperties = new ArrayList<>();
+ sortOrder.stream()
+ .map(order -> order.getSorted()
+ .getSortOrder(order.getDirection()))
+ .forEach(s -> s.forEach(sortProperties::add));
+ getDataCommunicator().setBackEndSorting(sortProperties);
+ }
+
+ @Override
+ public void itemClick(String rowKey, String columnId,
+ MouseEventDetails details) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void contextClick(int rowIndex, String rowKey, String columnId,
+ Section section, MouseEventDetails details) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void columnsReordered(List<String> newColumnOrder,
+ List<String> oldColumnOrder) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void columnVisibilityChanged(String id, boolean hidden,
+ boolean userOriginated) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void columnResized(String id, double pixels) {
+ // TODO Auto-generated method stub
+ }
+ }
+
+ /**
+ * This extension manages the configuration and data communication for a
+ * Column inside of a Grid component.
+ *
+ * @param <T>
+ * the grid bean type
+ * @param <V>
+ * the column value type
+ */
+ public static class Column<T, V> extends AbstractExtension
+ implements TypedDataGenerator<T> {
+
+ private Function<T, V> valueProvider;
+ private Function<SortDirection, Stream<SortOrder<String>>> sortOrderProvider;
+ private Comparator<T> comparator;
+
+ /**
+ * Constructs a new Column configuration with given header caption and
+ * value provider.
+ *
+ * @param caption
+ * the header caption
+ * @param valueType
+ * the type of value
+ * @param valueProvider
+ * the function to get values from data objects
+ */
+ protected Column(String caption, Class<V> valueType,
+ Function<T, V> valueProvider) {
+ Objects.requireNonNull(caption, "Header caption can't be null");
+ Objects.requireNonNull(valueProvider,
+ "Value provider can't be null");
+ Objects.requireNonNull(valueType, "Value type can't be null");
+
+ this.valueProvider = valueProvider;
+ getState().caption = caption;
+ sortOrderProvider = d -> Stream.of();
+
+ if (Comparable.class.isAssignableFrom(valueType)) {
+ comparator = (a, b) -> {
+ Comparable<V> comp = (Comparable<V>) valueProvider.apply(a);
+ return comp.compareTo(valueProvider.apply(b));
+ };
+ getState().sortable = true;
+ } else {
+ getState().sortable = false;
+ }
+ }
+
+ @Override
+ public void generateData(T data, JsonObject jsonObject) {
+ assert getState(
+ false).id != null : "No communication ID set for column "
+ + getState(false).caption;
+
+ if (!jsonObject.hasKey(DataCommunicatorConstants.DATA)) {
+ jsonObject.put(DataCommunicatorConstants.DATA,
+ Json.createObject());
+ }
+ JsonObject obj = jsonObject
+ .getObject(DataCommunicatorConstants.DATA);
+ // Since we dont' have renderers yet, use a dummy toString for data.
+ obj.put(getState(false).id, valueProvider.apply(data).toString());
+ }
+
+ @Override
+ public void destroyData(T data) {
+ }
+
+ @Override
+ protected ColumnState getState() {
+ return getState(true);
+ }
+
+ @Override
+ protected ColumnState getState(boolean markAsDirty) {
+ return (ColumnState) super.getState(markAsDirty);
+ }
+
+ /**
+ * This method extends the given Grid with this Column.
+ *
+ * @param grid
+ * the grid to extend
+ */
+ private void extend(Grid<T> grid) {
+ super.extend(grid);
+ }
+
+ /**
+ * Sets the identifier to use with this Column in communication.
+ *
+ * @param id
+ * the identifier string
+ */
+ private void setId(String id) {
+ Objects.requireNonNull(id, "Communication ID can't be null");
+ getState().id = id;
+ }
+
+ /**
+ * Sets whether the user can sort this column or not.
+ *
+ * @param sortable
+ * {@code true} if the column can be sorted by the user;
+ * {@code false} if not
+ * @return this column
+ */
+ public Column<T, V> setSortable(boolean sortable) {
+ getState().sortable = sortable;
+ return this;
+ }
+
+ /**
+ * Gets whether the user can sort this column or not.
+ *
+ * @return {@code true} if the column can be sorted by the user;
+ * {@code false} if not
+ */
+ public boolean isSortable() {
+ return getState(false).sortable;
+ }
+
+ /**
+ * Sets the header caption for this column.
+ *
+ * @param caption
+ * the header caption
+ *
+ * @return this column
+ */
+ public Column<T, V> setCaption(String caption) {
+ Objects.requireNonNull(caption, "Header caption can't be null");
+ getState().caption = caption;
+ return this;
+ }
+
+ /**
+ * Gets the header caption for this column.
+ *
+ * @return header caption
+ */
+ public String getCaption() {
+ return getState(false).caption;
+ }
+
+ /**
+ * Sets a comparator to use with in-memory sorting with this column.
+ * Sorting with a back-end is done using
+ * {@link Column#setSortProperty(String...)}.
+ *
+ * @param comparator
+ * the comparator to use when sorting data in this column
+ * @return this column
+ */
+ public Column<T, V> setComparator(Comparator<T> comparator) {
+ Objects.requireNonNull(comparator, "Comparator can't be null");
+ this.comparator = comparator;
+ return this;
+ }
+
+ /**
+ * Gets the comparator to use with in-memory sorting for this column
+ * when sorting in the given direction.
+ *
+ * @param sortDirection
+ * the direction this column is sorted by
+ * @return comparator for this column
+ */
+ public Comparator<T> getComparator(SortDirection sortDirection) {
+ Objects.requireNonNull(comparator,
+ "No comparator defined for sorted column.");
+ boolean reverse = sortDirection != SortDirection.ASCENDING;
+ return reverse ? comparator.reversed() : comparator;
+ }
+
+ /**
+ * Sets strings describing back end properties to be used when sorting
+ * this column. This method is a short hand for
+ * {@link #setSortBuilder(Function)} that takes an array of strings and
+ * uses the same sorting direction for all of them.
+ *
+ * @param properties
+ * the array of strings describing backend properties
+ * @return this column
+ */
+ public Column<T, V> setSortProperty(String... properties) {
+ Objects.requireNonNull(properties, "Sort properties can't be null");
+ sortOrderProvider = dir -> Arrays.stream(properties)
+ .map(s -> new SortOrder<>(s, dir));
+ return this;
+ }
+
+ /**
+ * Sets the sort orders when sorting this column. The sort order
+ * provider is a function which provides {@link SortOrder} objects to
+ * describe how to sort by this column.
+ *
+ * @param provider
+ * the function to use when generating sort orders with the
+ * given direction
+ * @return this column
+ */
+ public Column<T, V> setSortOrderProvider(
+ Function<SortDirection, Stream<SortOrder<String>>> provider) {
+ Objects.requireNonNull(provider,
+ "Sort order provider can't be null");
+ sortOrderProvider = provider;
+ return this;
+ }
+
+ /**
+ * Gets the sort orders to use with back-end sorting for this column
+ * when sorting in the given direction.
+ *
+ * @param direction
+ * the sorting direction
+ * @return stream of sort orders
+ */
+ public Stream<SortOrder<String>> getSortOrder(SortDirection direction) {
+ return sortOrderProvider.apply(direction);
+ }
+ }
+
+ private KeyMapper<Column<T, ?>> columnKeys = new KeyMapper<>();
+ private Set<Column<T, ?>> columnSet = new HashSet<>();
+ private List<SortOrder<Column<T, ?>>> sortOrder = new ArrayList<>();
+
+ /**
+ * Constructor for the {@link Grid} component.
+ */
+ public Grid() {
+ setDataSource(DataSource.create());
+ registerRpc(new GridServerRpcImpl());
+ }
+
+ /**
+ * Adds a new column to this {@link Grid} with given header caption and
+ * value provider.
+ *
+ * @param caption
+ * the header caption
+ * @param valueType
+ * the column value class
+ * @param valueProvider
+ * the value provider
+ * @param <V>
+ * the column value type
+ *
+ * @return the new column
+ */
+ public <V> Column<T, V> addColumn(String caption, Class<V> valueType,
+ Function<T, V> valueProvider) {
+ Column<T, V> c = new Column<T, V>(caption, valueType, valueProvider);
+
+ c.extend(this);
+ c.setId(columnKeys.key(c));
+ columnSet.add(c);
+ addDataGenerator(c);
+
+ return c;
+ }
+
+ /**
+ * Removes the given column from this {@link Grid}.
+ *
+ * @param column
+ * the column to remove
+ */
+ public void removeColumn(Column<T, ?> column) {
+ if (columnSet.remove(column)) {
+ columnKeys.remove(column);
+ removeDataGenerator(column);
+ column.remove();
+ }
+ }
+
+ /**
+ * Gets an unmodifiable collection of all columns currently in this
+ * {@link Grid}.
+ *
+ * @return unmodifiable collection of columns
+ */
+ public Collection<Column<T, ?>> getColumns() {
+ return Collections.unmodifiableSet(columnSet);
+ }
+}