aboutsummaryrefslogtreecommitdiffstats
path: root/compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2016-08-18 23:19:41 +0300
committerArtur Signell <artur@vaadin.com>2016-08-22 15:59:51 +0300
commitc6b44ac8adc9b2ffd6290c98643a633f405dd6c6 (patch)
tree634982e9a789452ab8046e660a9170cc8b25623f /compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java
parentec8904f6b0ab77231d567daa35c9cc7138b6fe59 (diff)
downloadvaadin-framework-c6b44ac8adc9b2ffd6290c98643a633f405dd6c6.tar.gz
vaadin-framework-c6b44ac8adc9b2ffd6290c98643a633f405dd6c6.zip
Move and rename server classes which go into the compatibility package
* Use com.vaadin.v7 * Use the same class name as in Vaadin 7 * Use a "vaadin7-" declarative prefix for Vaadin 7 components Change-Id: I19a27f3835b18980b91a4f8f9464b2adde1a5fd5
Diffstat (limited to 'compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java')
-rw-r--r--compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java7352
1 files changed, 0 insertions, 7352 deletions
diff --git a/compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java b/compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java
deleted file mode 100644
index 9a98593b83..0000000000
--- a/compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java
+++ /dev/null
@@ -1,7352 +0,0 @@
-/*
- * 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.io.Serializable;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.jsoup.nodes.Attributes;
-import org.jsoup.nodes.Element;
-import org.jsoup.select.Elements;
-
-import com.vaadin.data.Container;
-import com.vaadin.data.Container.Indexed;
-import com.vaadin.data.Container.ItemSetChangeEvent;
-import com.vaadin.data.Container.ItemSetChangeListener;
-import com.vaadin.data.Container.ItemSetChangeNotifier;
-import com.vaadin.data.Container.PropertySetChangeEvent;
-import com.vaadin.data.Container.PropertySetChangeListener;
-import com.vaadin.data.Container.PropertySetChangeNotifier;
-import com.vaadin.data.Container.Sortable;
-import com.vaadin.data.Item;
-import com.vaadin.data.Property;
-import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory;
-import com.vaadin.data.fieldgroup.FieldGroup;
-import com.vaadin.data.fieldgroup.FieldGroup.CommitException;
-import com.vaadin.data.fieldgroup.FieldGroupFieldFactory;
-import com.vaadin.data.sort.Sort;
-import com.vaadin.data.sort.SortOrder;
-import com.vaadin.data.util.IndexedContainer;
-import com.vaadin.event.ContextClickEvent;
-import com.vaadin.event.ItemClickEvent;
-import com.vaadin.event.ItemClickEvent.ItemClickListener;
-import com.vaadin.event.ItemClickEvent.ItemClickNotifier;
-import com.vaadin.event.SelectionEvent;
-import com.vaadin.event.SelectionEvent.SelectionListener;
-import com.vaadin.event.SelectionEvent.SelectionNotifier;
-import com.vaadin.event.SortEvent;
-import com.vaadin.event.SortEvent.SortListener;
-import com.vaadin.event.SortEvent.SortNotifier;
-import com.vaadin.server.AbstractClientConnector;
-import com.vaadin.server.AbstractExtension;
-import com.vaadin.server.EncodeResult;
-import com.vaadin.server.ErrorMessage;
-import com.vaadin.server.Extension;
-import com.vaadin.server.JsonCodec;
-import com.vaadin.server.KeyMapper;
-import com.vaadin.server.VaadinSession;
-import com.vaadin.server.communication.data.DataGenerator;
-import com.vaadin.server.communication.data.RpcDataProviderExtension;
-import com.vaadin.shared.MouseEventDetails;
-import com.vaadin.shared.data.sort.SortDirection;
-import com.vaadin.shared.ui.grid.EditorClientRpc;
-import com.vaadin.shared.ui.grid.EditorServerRpc;
-import com.vaadin.shared.ui.grid.GridClientRpc;
-import com.vaadin.shared.ui.grid.GridColumnState;
-import com.vaadin.shared.ui.grid.GridConstants;
-import com.vaadin.shared.ui.grid.GridConstants.Section;
-import com.vaadin.shared.ui.grid.GridServerRpc;
-import com.vaadin.shared.ui.grid.GridState;
-import com.vaadin.shared.ui.grid.GridStaticCellType;
-import com.vaadin.shared.ui.grid.GridStaticSectionState;
-import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState;
-import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState;
-import com.vaadin.shared.ui.grid.HeightMode;
-import com.vaadin.shared.ui.grid.ScrollDestination;
-import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc;
-import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState;
-import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc;
-import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState;
-import com.vaadin.shared.util.SharedUtil;
-import com.vaadin.ui.declarative.DesignAttributeHandler;
-import com.vaadin.ui.declarative.DesignContext;
-import com.vaadin.ui.declarative.DesignException;
-import com.vaadin.ui.declarative.DesignFormatter;
-import com.vaadin.ui.renderers.HtmlRenderer;
-import com.vaadin.ui.renderers.Renderer;
-import com.vaadin.ui.renderers.TextRenderer;
-import com.vaadin.util.ReflectTools;
-import com.vaadin.v7.data.Validator.InvalidValueException;
-import com.vaadin.v7.data.util.converter.LegacyConverter;
-import com.vaadin.v7.data.util.converter.LegacyConverterUtil;
-import com.vaadin.v7.ui.LegacyCheckBox;
-import com.vaadin.v7.ui.LegacyField;
-
-import elemental.json.Json;
-import elemental.json.JsonObject;
-import elemental.json.JsonValue;
-
-/**
- * A grid component for displaying tabular data.
- * <p>
- * Grid is always bound to a {@link Container.Indexed}, but is not a
- * {@code Container} of any kind in of itself. The contents of the given
- * Container is displayed with the help of {@link Renderer Renderers}.
- *
- * <h3 id="grid-headers-and-footers">Headers and Footers</h3>
- * <p>
- *
- *
- * <h3 id="grid-converters-and-renderers">Converters and Renderers</h3>
- * <p>
- * Each column has its own {@link Renderer} that displays data into something
- * that can be displayed in the browser. That data is first converted with a
- * {@link com.vaadin.v7.data.util.converter.LegacyConverter Converter} into
- * something that the Renderer can process. This can also be an implicit step -
- * if a column has a simple data type, like a String, no explicit assignment is
- * needed.
- * <p>
- * Usually a renderer takes some kind of object, and converts it into a
- * HTML-formatted string.
- * <p>
- * <code><pre>
- * Grid grid = new Grid(myContainer);
- * Column column = grid.getColumn(STRING_DATE_PROPERTY);
- * column.setConverter(new StringToDateConverter());
- * column.setRenderer(new MyColorfulDateRenderer());
- * </pre></code>
- *
- * <h3 id="grid-lazyloading">Lazy Loading</h3>
- * <p>
- * The data is accessed as it is needed by Grid and not any sooner. In other
- * words, if the given Container is huge, but only the first few rows are
- * displayed to the user, only those (and a few more, for caching purposes) are
- * accessed.
- *
- * <h3 id="grid-selection-modes-and-models">Selection Modes and Models</h3>
- * <p>
- * Grid supports three selection <em>{@link SelectionMode modes}</em> (single,
- * multi, none), and comes bundled with one <em>{@link SelectionModel
- * model}</em> for each of the modes. The distinction between a selection mode
- * and selection model is as follows: a <em>mode</em> essentially says whether
- * you can have one, many or no rows selected. The model, however, has the
- * behavioral details of each. A single selection model may require that the
- * user deselects one row before selecting another one. A variant of a
- * multiselect might have a configurable maximum of rows that may be selected.
- * And so on.
- * <p>
- * <code><pre>
- * Grid grid = new Grid(myContainer);
- *
- * // uses the bundled SingleSelectionModel class
- * grid.setSelectionMode(SelectionMode.SINGLE);
- *
- * // changes the behavior to a custom selection model
- * grid.setSelectionModel(new MyTwoSelectionModel());
- * </pre></code>
- *
- * @since 7.4
- * @author Vaadin Ltd
- */
-public class LegacyGrid extends AbstractFocusable implements SelectionNotifier,
- SortNotifier, SelectiveRenderer, ItemClickNotifier {
-
- /**
- * An event listener for column visibility change events in the Grid.
- *
- * @since 7.5.0
- */
- public interface ColumnVisibilityChangeListener extends Serializable {
- /**
- * Called when a column has become hidden or unhidden.
- *
- * @param event
- */
- void columnVisibilityChanged(ColumnVisibilityChangeEvent event);
- }
-
- /**
- * An event that is fired when a column's visibility changes.
- *
- * @since 7.5.0
- */
- public static class ColumnVisibilityChangeEvent extends Component.Event {
-
- private final Column column;
- private final boolean userOriginated;
- private final boolean hidden;
-
- /**
- * Constructor for a column visibility change event.
- *
- * @param source
- * the grid from which this event originates
- * @param column
- * the column that changed its visibility
- * @param hidden
- * <code>true</code> if the column was hidden,
- * <code>false</code> if it became visible
- * @param isUserOriginated
- * <code>true</code> iff the event was triggered by an UI
- * interaction
- */
- public ColumnVisibilityChangeEvent(LegacyGrid source, Column column,
- boolean hidden, boolean isUserOriginated) {
- super(source);
- this.column = column;
- this.hidden = hidden;
- userOriginated = isUserOriginated;
- }
-
- /**
- * Gets the column that became hidden or visible.
- *
- * @return the column that became hidden or visible.
- * @see Column#isHidden()
- */
- public Column getColumn() {
- return column;
- }
-
- /**
- * Was the column set hidden or visible.
- *
- * @return <code>true</code> if the column was hidden <code>false</code>
- * if it was set visible
- */
- public boolean isHidden() {
- return hidden;
- }
-
- /**
- * Returns <code>true</code> if the column reorder was done by the user,
- * <code>false</code> if not and it was triggered by server side code.
- *
- * @return <code>true</code> if event is a result of user interaction
- */
- public boolean isUserOriginated() {
- return userOriginated;
- }
- }
-
- /**
- * A callback interface for generating details for a particular row in Grid.
- *
- * @since 7.5.0
- * @author Vaadin Ltd
- * @see DetailsGenerator#NULL
- */
- public interface DetailsGenerator extends Serializable {
-
- /** A details generator that provides no details */
- public DetailsGenerator NULL = new DetailsGenerator() {
- @Override
- public Component getDetails(RowReference rowReference) {
- return null;
- }
- };
-
- /**
- * This method is called for whenever a details row needs to be shown on
- * the client. Grid removes all of its references to details components
- * when they are no longer displayed on the client-side and will
- * re-request once needed again.
- * <p>
- * <em>Note:</em> If a component gets generated, it may not be manually
- * attached anywhere. The same details component can not be displayed
- * for multiple different rows.
- *
- * @param rowReference
- * the reference for the row for which to generate details
- * @return the details for the given row, or <code>null</code> to leave
- * the details empty.
- */
- Component getDetails(RowReference rowReference);
- }
-
- /**
- * A class that manages details components by calling
- * {@link DetailsGenerator} as needed. Details components are attached by
- * this class when the {@link RpcDataProviderExtension} is sending data to
- * the client. Details components are detached and forgotten when client
- * informs that it has dropped the corresponding item.
- *
- * @since 7.6.1
- */
- public final static class DetailComponentManager
- extends AbstractGridExtension implements DataGenerator {
-
- /**
- * The user-defined details generator.
- *
- * @see #setDetailsGenerator(DetailsGenerator)
- */
- private DetailsGenerator detailsGenerator;
-
- /**
- * This map represents all details that are currently visible on the
- * client. Details components get destroyed once they scroll out of
- * view.
- */
- private final Map<Object, Component> itemIdToDetailsComponent = new HashMap<>();
-
- /**
- * Set of item ids that got <code>null</code> from DetailsGenerator when
- * {@link DetailsGenerator#getDetails(RowReference)} was called.
- */
- private final Set<Object> emptyDetails = new HashSet<>();
-
- /**
- * Set of item IDs for all open details rows. Contains even the ones
- * that are not currently visible on the client.
- */
- private final Set<Object> openDetails = new HashSet<>();
-
- public DetailComponentManager(LegacyGrid grid) {
- this(grid, DetailsGenerator.NULL);
- }
-
- public DetailComponentManager(LegacyGrid grid,
- DetailsGenerator detailsGenerator) {
- super(grid);
- setDetailsGenerator(detailsGenerator);
- }
-
- /**
- * Creates a details component with the help of the user-defined
- * {@link DetailsGenerator}.
- * <p>
- * This method attaches created components to the parent {@link LegacyGrid}.
- *
- * @param itemId
- * the item id for which to create the details component.
- * @throws IllegalStateException
- * if the current details generator provides a component
- * that was manually attached.
- */
- private void createDetails(Object itemId) throws IllegalStateException {
- assert itemId != null : "itemId was null";
-
- if (itemIdToDetailsComponent.containsKey(itemId)
- || emptyDetails.contains(itemId)) {
- // Don't overwrite existing components
- return;
- }
-
- RowReference rowReference = new RowReference(getParentGrid());
- rowReference.set(itemId);
-
- DetailsGenerator detailsGenerator = getParentGrid()
- .getDetailsGenerator();
- Component details = detailsGenerator.getDetails(rowReference);
- if (details != null) {
- if (details.getParent() != null) {
- String name = detailsGenerator.getClass().getName();
- throw new IllegalStateException(
- name + " generated a details component that already "
- + "was attached. (itemId: " + itemId
- + ", component: " + details + ")");
- }
-
- itemIdToDetailsComponent.put(itemId, details);
-
- addComponentToGrid(details);
-
- assert !emptyDetails.contains(itemId) : "Bookeeping thinks "
- + "itemId is empty even though we just created a "
- + "component for it (" + itemId + ")";
- } else {
- emptyDetails.add(itemId);
- }
-
- }
-
- /**
- * Destroys a details component correctly.
- * <p>
- * This method will detach the component from parent {@link LegacyGrid}.
- *
- * @param itemId
- * the item id for which to destroy the details component
- */
- private void destroyDetails(Object itemId) {
- emptyDetails.remove(itemId);
-
- Component removedComponent = itemIdToDetailsComponent
- .remove(itemId);
- if (removedComponent == null) {
- return;
- }
-
- removeComponentFromGrid(removedComponent);
- }
-
- /**
- * Recreates all visible details components.
- */
- public void refreshDetails() {
- Set<Object> visibleItemIds = new HashSet<>(
- itemIdToDetailsComponent.keySet());
- for (Object itemId : visibleItemIds) {
- destroyDetails(itemId);
- createDetails(itemId);
- refreshRow(itemId);
- }
- }
-
- /**
- * Sets details visiblity status of given item id.
- *
- * @param itemId
- * item id to set
- * @param visible
- * <code>true</code> if visible; <code>false</code> if not
- */
- public void setDetailsVisible(Object itemId, boolean visible) {
- if ((visible && openDetails.contains(itemId))
- || (!visible && !openDetails.contains(itemId))) {
- return;
- }
-
- if (visible) {
- openDetails.add(itemId);
- refreshRow(itemId);
- } else {
- openDetails.remove(itemId);
- destroyDetails(itemId);
- refreshRow(itemId);
- }
- }
-
- @Override
- public void generateData(Object itemId, Item item, JsonObject rowData) {
- // DetailComponentManager should not send anything if details
- // generator is the default null version.
- if (openDetails.contains(itemId)
- && !detailsGenerator.equals(DetailsGenerator.NULL)) {
- // Double check to be sure details component exists.
- createDetails(itemId);
-
- Component detailsComponent = itemIdToDetailsComponent
- .get(itemId);
- rowData.put(GridState.JSONKEY_DETAILS_VISIBLE,
- (detailsComponent != null
- ? detailsComponent.getConnectorId() : ""));
- }
- }
-
- @Override
- public void destroyData(Object itemId) {
- if (openDetails.contains(itemId)) {
- destroyDetails(itemId);
- }
- }
-
- /**
- * Sets a new details generator for row details.
- * <p>
- * The currently opened row details will be re-rendered.
- *
- * @param detailsGenerator
- * the details generator to set
- * @throws IllegalArgumentException
- * if detailsGenerator is <code>null</code>;
- */
- public void setDetailsGenerator(DetailsGenerator detailsGenerator)
- throws IllegalArgumentException {
- if (detailsGenerator == null) {
- throw new IllegalArgumentException(
- "Details generator may not be null");
- } else if (detailsGenerator == this.detailsGenerator) {
- return;
- }
-
- this.detailsGenerator = detailsGenerator;
-
- refreshDetails();
- }
-
- /**
- * Gets the current details generator for row details.
- *
- * @return the detailsGenerator the current details generator
- */
- public DetailsGenerator getDetailsGenerator() {
- return detailsGenerator;
- }
-
- /**
- * Checks whether details are visible for the given item.
- *
- * @param itemId
- * the id of the item for which to check details visibility
- * @return <code>true</code> iff the details are visible
- */
- public boolean isDetailsVisible(Object itemId) {
- return openDetails.contains(itemId);
- }
- }
-
- /**
- * Custom field group that allows finding property types before an item has
- * been bound.
- */
- private final class CustomFieldGroup extends FieldGroup {
-
- public CustomFieldGroup() {
- setFieldFactory(EditorFieldFactory.get());
- }
-
- @Override
- protected Class<?> getPropertyType(Object propertyId)
- throws BindException {
- if (getItemDataSource() == null) {
- return datasource.getType(propertyId);
- } else {
- return super.getPropertyType(propertyId);
- }
- }
-
- @Override
- protected <T extends LegacyField> T build(String caption,
- Class<?> dataType, Class<T> fieldType) throws BindException {
- T field = super.build(caption, dataType, fieldType);
- if (field instanceof LegacyCheckBox) {
- field.setCaption(null);
- }
- return field;
- }
- }
-
- /**
- * Field factory used by default in the editor.
- *
- * Aims to fields of suitable type and with suitable size for use in the
- * editor row.
- */
- public static class EditorFieldFactory
- extends DefaultFieldGroupFieldFactory {
- private static final EditorFieldFactory INSTANCE = new EditorFieldFactory();
-
- protected EditorFieldFactory() {
- }
-
- /**
- * Returns the singleton instance
- *
- * @return the singleton instance
- */
- public static EditorFieldFactory get() {
- return INSTANCE;
- }
-
- @Override
- public <T extends LegacyField> T createField(Class<?> type,
- Class<T> fieldType) {
- T f = super.createField(type, fieldType);
- if (f != null) {
- f.setWidth("100%");
- }
- return f;
- }
-
- @Override
- protected AbstractSelect createCompatibleSelect(
- Class<? extends AbstractSelect> fieldType) {
- if (anySelect(fieldType)) {
- return super.createCompatibleSelect(ComboBox.class);
- }
- return super.createCompatibleSelect(fieldType);
- }
-
- @Override
- protected void populateWithEnumData(AbstractSelect select,
- Class<? extends Enum> enumClass) {
- // Use enums directly and the EnumToStringConverter to be consistent
- // with what is shown in the Grid
- @SuppressWarnings("unchecked")
- EnumSet<?> enumSet = EnumSet.allOf(enumClass);
- for (Object r : enumSet) {
- select.addItem(r);
- }
- }
- }
-
- /**
- * Error handler for the editor
- */
- public interface EditorErrorHandler extends Serializable {
-
- /**
- * Called when an exception occurs while the editor row is being saved
- *
- * @param event
- * An event providing more information about the error
- */
- void commitError(CommitErrorEvent event);
- }
-
- /**
- * ContextClickEvent for the Grid Component.
- *
- * @since 7.6
- */
- public static class GridContextClickEvent extends ContextClickEvent {
-
- private final Object itemId;
- private final int rowIndex;
- private final Object propertyId;
- private final Section section;
-
- public GridContextClickEvent(LegacyGrid source,
- MouseEventDetails mouseEventDetails, Section section,
- int rowIndex, Object itemId, Object propertyId) {
- super(source, mouseEventDetails);
- this.itemId = itemId;
- this.propertyId = propertyId;
- this.section = section;
- this.rowIndex = rowIndex;
- }
-
- /**
- * Returns the item id of context clicked row.
- *
- * @return item id of clicked row; <code>null</code> if header or footer
- */
- public Object getItemId() {
- return itemId;
- }
-
- /**
- * Returns property id of clicked column.
- *
- * @return property id
- */
- public Object getPropertyId() {
- return propertyId;
- }
-
- /**
- * Return the clicked section of Grid.
- *
- * @return section of grid
- */
- public Section getSection() {
- return section;
- }
-
- /**
- * Returns the clicked row index relative to Grid section. In the body
- * of the Grid the index is the item index in the Container. Header and
- * Footer rows for index can be fetched with
- * {@link LegacyGrid#getHeaderRow(int)} and {@link LegacyGrid#getFooterRow(int)}.
- *
- * @return row index in section
- */
- public int getRowIndex() {
- return rowIndex;
- }
-
- @Override
- public LegacyGrid getComponent() {
- return (LegacyGrid) super.getComponent();
- }
- }
-
- /**
- * An event which is fired when saving the editor fails
- */
- public static class CommitErrorEvent extends Component.Event {
-
- private CommitException cause;
-
- private Set<Column> errorColumns = new HashSet<>();
-
- private String userErrorMessage;
-
- public CommitErrorEvent(LegacyGrid grid, CommitException cause) {
- super(grid);
- this.cause = cause;
- userErrorMessage = cause.getLocalizedMessage();
- }
-
- /**
- * Retrieves the cause of the failure
- *
- * @return the cause of the failure
- */
- public CommitException getCause() {
- return cause;
- }
-
- @Override
- public LegacyGrid getComponent() {
- return (LegacyGrid) super.getComponent();
- }
-
- /**
- * Checks if validation exceptions caused this error
- *
- * @return true if the problem was caused by a validation error
- */
- public boolean isValidationFailure() {
- return cause.getCause() instanceof InvalidValueException;
- }
-
- /**
- * Marks that an error indicator should be shown for the editor of a
- * column.
- *
- * @param column
- * the column to show an error for
- */
- public void addErrorColumn(Column column) {
- errorColumns.add(column);
- }
-
- /**
- * Gets all the columns that have been marked as erroneous.
- *
- * @return an umodifiable collection of erroneous columns
- */
- public Collection<Column> getErrorColumns() {
- return Collections.unmodifiableCollection(errorColumns);
- }
-
- /**
- * Gets the error message to show to the user.
- *
- * @return error message to show
- */
- public String getUserErrorMessage() {
- return userErrorMessage;
- }
-
- /**
- * Sets the error message to show to the user.
- *
- * @param userErrorMessage
- * the user error message to set
- */
- public void setUserErrorMessage(String userErrorMessage) {
- this.userErrorMessage = userErrorMessage;
- }
-
- }
-
- /**
- * An event listener for column reorder events in the Grid.
- *
- * @since 7.5.0
- */
- public interface ColumnReorderListener extends Serializable {
-
- /**
- * Called when the columns of the grid have been reordered.
- *
- * @param event
- * An event providing more information
- */
- void columnReorder(ColumnReorderEvent event);
- }
-
- /**
- * An event that is fired when the columns are reordered.
- *
- * @since 7.5.0
- */
- public static class ColumnReorderEvent extends Component.Event {
-
- private final boolean userOriginated;
-
- /**
- *
- * @param source
- * the grid where the event originated from
- * @param userOriginated
- * <code>true</code> if event is a result of user
- * interaction, <code>false</code> if from API call
- */
- public ColumnReorderEvent(LegacyGrid source, boolean userOriginated) {
- super(source);
- this.userOriginated = userOriginated;
- }
-
- /**
- * Returns <code>true</code> if the column reorder was done by the user,
- * <code>false</code> if not and it was triggered by server side code.
- *
- * @return <code>true</code> if event is a result of user interaction
- */
- public boolean isUserOriginated() {
- return userOriginated;
- }
-
- }
-
- /**
- * An event listener for column resize events in the Grid.
- *
- * @since 7.6
- */
- public interface ColumnResizeListener extends Serializable {
-
- /**
- * Called when the columns of the grid have been resized.
- *
- * @param event
- * An event providing more information
- */
- void columnResize(ColumnResizeEvent event);
- }
-
- /**
- * An event that is fired when a column is resized, either programmatically
- * or by the user.
- *
- * @since 7.6
- */
- public static class ColumnResizeEvent extends Component.Event {
-
- private final Column column;
- private final boolean userOriginated;
-
- /**
- *
- * @param source
- * the grid where the event originated from
- * @param userOriginated
- * <code>true</code> if event is a result of user
- * interaction, <code>false</code> if from API call
- */
- public ColumnResizeEvent(LegacyGrid source, Column column,
- boolean userOriginated) {
- super(source);
- this.column = column;
- this.userOriginated = userOriginated;
- }
-
- /**
- * Returns the column that was resized.
- *
- * @return the resized column.
- */
- public Column getColumn() {
- return column;
- }
-
- /**
- * Returns <code>true</code> if the column resize was done by the user,
- * <code>false</code> if not and it was triggered by server side code.
- *
- * @return <code>true</code> if event is a result of user interaction
- */
- public boolean isUserOriginated() {
- return userOriginated;
- }
-
- }
-
- /**
- * Interface for an editor event listener
- */
- public interface EditorListener extends Serializable {
-
- public static final Method EDITOR_OPEN_METHOD = ReflectTools.findMethod(
- EditorListener.class, "editorOpened", EditorOpenEvent.class);
- public static final Method EDITOR_MOVE_METHOD = ReflectTools.findMethod(
- EditorListener.class, "editorMoved", EditorMoveEvent.class);
- public static final Method EDITOR_CLOSE_METHOD = ReflectTools
- .findMethod(EditorListener.class, "editorClosed",
- EditorCloseEvent.class);
-
- /**
- * Called when an editor is opened
- *
- * @param e
- * an editor open event object
- */
- public void editorOpened(EditorOpenEvent e);
-
- /**
- * Called when an editor is reopened without closing it first
- *
- * @param e
- * an editor move event object
- */
- public void editorMoved(EditorMoveEvent e);
-
- /**
- * Called when an editor is closed
- *
- * @param e
- * an editor close event object
- */
- public void editorClosed(EditorCloseEvent e);
-
- }
-
- /**
- * Base class for editor related events
- */
- public static abstract class EditorEvent extends Component.Event {
-
- private Object itemID;
-
- protected EditorEvent(LegacyGrid source, Object itemID) {
- super(source);
- this.itemID = itemID;
- }
-
- /**
- * Get the item (row) for which this editor was opened
- */
- public Object getItem() {
- return itemID;
- }
-
- }
-
- /**
- * This event gets fired when an editor is opened
- */
- public static class EditorOpenEvent extends EditorEvent {
-
- public EditorOpenEvent(LegacyGrid source, Object itemID) {
- super(source, itemID);
- }
- }
-
- /**
- * This event gets fired when an editor is opened while another row is being
- * edited (i.e. editor focus moves elsewhere)
- */
- public static class EditorMoveEvent extends EditorEvent {
-
- public EditorMoveEvent(LegacyGrid source, Object itemID) {
- super(source, itemID);
- }
- }
-
- /**
- * This event gets fired when an editor is dismissed or closed by other
- * means.
- */
- public static class EditorCloseEvent extends EditorEvent {
-
- public EditorCloseEvent(LegacyGrid source, Object itemID) {
- super(source, itemID);
- }
- }
-
- /**
- * Default error handler for the editor
- *
- */
- public class DefaultEditorErrorHandler implements EditorErrorHandler {
-
- @Override
- public void commitError(CommitErrorEvent event) {
- Map<LegacyField<?>, InvalidValueException> invalidFields = event
- .getCause().getInvalidFields();
-
- if (!invalidFields.isEmpty()) {
- Object firstErrorPropertyId = null;
- LegacyField<?> firstErrorField = null;
-
- FieldGroup fieldGroup = event.getCause().getFieldGroup();
- for (Column column : getColumns()) {
- Object propertyId = column.getPropertyId();
- LegacyField<?> field = fieldGroup.getField(propertyId);
- if (invalidFields.keySet().contains(field)) {
- event.addErrorColumn(column);
-
- if (firstErrorPropertyId == null) {
- firstErrorPropertyId = propertyId;
- firstErrorField = field;
- }
- }
- }
-
- /*
- * Validation error, show first failure as
- * "<Column header>: <message>"
- */
- String caption = getColumn(firstErrorPropertyId)
- .getHeaderCaption();
- String message = invalidFields.get(firstErrorField)
- .getLocalizedMessage();
-
- event.setUserErrorMessage(caption + ": " + message);
- } else {
- com.vaadin.server.ErrorEvent.findErrorHandler(LegacyGrid.this).error(
- new ConnectorErrorEvent(LegacyGrid.this, event.getCause()));
- }
- }
-
- private Object getFirstPropertyId(FieldGroup fieldGroup,
- Set<LegacyField<?>> keySet) {
- for (Column c : getColumns()) {
- Object propertyId = c.getPropertyId();
- LegacyField<?> f = fieldGroup.getField(propertyId);
- if (keySet.contains(f)) {
- return propertyId;
- }
- }
- return null;
- }
- }
-
- /**
- * Selection modes representing built-in {@link SelectionModel
- * SelectionModels} that come bundled with {@link LegacyGrid}.
- * <p>
- * Passing one of these enums into
- * {@link LegacyGrid#setSelectionMode(SelectionMode)} is equivalent to calling
- * {@link LegacyGrid#setSelectionModel(SelectionModel)} with one of the built-in
- * implementations of {@link SelectionModel}.
- *
- * @see LegacyGrid#setSelectionMode(SelectionMode)
- * @see LegacyGrid#setSelectionModel(SelectionModel)
- */
- public enum SelectionMode {
- /** A SelectionMode that maps to {@link SingleSelectionModel} */
- SINGLE {
- @Override
- protected SelectionModel createModel() {
- return new SingleSelectionModel();
- }
-
- },
-
- /** A SelectionMode that maps to {@link MultiSelectionModel} */
- MULTI {
- @Override
- protected SelectionModel createModel() {
- return new MultiSelectionModel();
- }
- },
-
- /** A SelectionMode that maps to {@link NoSelectionModel} */
- NONE {
- @Override
- protected SelectionModel createModel() {
- return new NoSelectionModel();
- }
- };
-
- protected abstract SelectionModel createModel();
- }
-
- /**
- * The server-side interface that controls Grid's selection state.
- * SelectionModel should extend {@link AbstractGridExtension}.
- */
- public interface SelectionModel extends Serializable, Extension {
- /**
- * Checks whether an item is selected or not.
- *
- * @param itemId
- * the item id to check for
- * @return <code>true</code> iff the item is selected
- */
- boolean isSelected(Object itemId);
-
- /**
- * Returns a collection of all the currently selected itemIds.
- *
- * @return a collection of all the currently selected itemIds
- */
- Collection<Object> getSelectedRows();
-
- /**
- * Injects the current {@link LegacyGrid} instance into the SelectionModel.
- * This method should usually call the extend method of
- * {@link AbstractExtension}.
- * <p>
- * <em>Note:</em> This method should not be called manually.
- *
- * @param grid
- * the Grid in which the SelectionModel currently is, or
- * <code>null</code> when a selection model is being detached
- * from a Grid.
- */
- void setGrid(LegacyGrid grid);
-
- /**
- * Resets the SelectiomModel to an initial state.
- * <p>
- * Most often this means that the selection state is cleared, but
- * implementations are free to interpret the "initial state" as they
- * wish. Some, for example, may want to keep the first selected item as
- * selected.
- */
- void reset();
-
- /**
- * A SelectionModel that supports multiple selections to be made.
- * <p>
- * This interface has a contract of having the same behavior, no matter
- * how the selection model is interacted with. In other words, if
- * something is forbidden to do in e.g. the user interface, it must also
- * be forbidden to do in the server-side and client-side APIs.
- */
- public interface Multi extends SelectionModel {
-
- /**
- * Marks items as selected.
- * <p>
- * This method does not clear any previous selection state, only
- * adds to it.
- *
- * @param itemIds
- * the itemId(s) to mark as selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if all the given itemIds already were
- * selected
- * @throws IllegalArgumentException
- * if the <code>itemIds</code> varargs array is
- * <code>null</code> or given itemIds don't exist in the
- * container of Grid
- * @see #deselect(Object...)
- */
- boolean select(Object... itemIds) throws IllegalArgumentException;
-
- /**
- * Marks items as selected.
- * <p>
- * This method does not clear any previous selection state, only
- * adds to it.
- *
- * @param itemIds
- * the itemIds to mark as selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if all the given itemIds already were
- * selected
- * @throws IllegalArgumentException
- * if <code>itemIds</code> is <code>null</code> or given
- * itemIds don't exist in the container of Grid
- * @see #deselect(Collection)
- */
- boolean select(Collection<?> itemIds)
- throws IllegalArgumentException;
-
- /**
- * Marks items as deselected.
- *
- * @param itemIds
- * the itemId(s) to remove from being selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if none the given itemIds were
- * selected previously
- * @throws IllegalArgumentException
- * if the <code>itemIds</code> varargs array is
- * <code>null</code>
- * @see #select(Object...)
- */
- boolean deselect(Object... itemIds) throws IllegalArgumentException;
-
- /**
- * Marks items as deselected.
- *
- * @param itemIds
- * the itemId(s) to remove from being selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if none the given itemIds were
- * selected previously
- * @throws IllegalArgumentException
- * if <code>itemIds</code> is <code>null</code>
- * @see #select(Collection)
- */
- boolean deselect(Collection<?> itemIds)
- throws IllegalArgumentException;
-
- /**
- * Marks all the items in the current Container as selected
- *
- * @return <code>true</code> iff some items were previously not
- * selected
- * @see #deselectAll()
- */
- boolean selectAll();
-
- /**
- * Marks all the items in the current Container as deselected
- *
- * @return <code>true</code> iff some items were previously selected
- * @see #selectAll()
- */
- boolean deselectAll();
-
- /**
- * Marks items as selected while deselecting all items not in the
- * given Collection.
- *
- * @param itemIds
- * the itemIds to mark as selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if all the given itemIds already were
- * selected
- * @throws IllegalArgumentException
- * if <code>itemIds</code> is <code>null</code> or given
- * itemIds don't exist in the container of Grid
- */
- boolean setSelected(Collection<?> itemIds)
- throws IllegalArgumentException;
-
- /**
- * Marks items as selected while deselecting all items not in the
- * varargs array.
- *
- * @param itemIds
- * the itemIds to mark as selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if all the given itemIds already were
- * selected
- * @throws IllegalArgumentException
- * if the <code>itemIds</code> varargs array is
- * <code>null</code> or given itemIds don't exist in the
- * container of Grid
- */
- boolean setSelected(Object... itemIds)
- throws IllegalArgumentException;
- }
-
- /**
- * A SelectionModel that supports for only single rows to be selected at
- * a time.
- * <p>
- * This interface has a contract of having the same behavior, no matter
- * how the selection model is interacted with. In other words, if
- * something is forbidden to do in e.g. the user interface, it must also
- * be forbidden to do in the server-side and client-side APIs.
- */
- public interface Single extends SelectionModel {
-
- /**
- * Marks an item as selected.
- *
- * @param itemId
- * the itemId to mark as selected; <code>null</code> for
- * deselect
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if the itemId already was selected
- * @throws IllegalStateException
- * if the selection was illegal. One such reason might
- * be that the given id was null, indicating a deselect,
- * but implementation doesn't allow deselecting.
- * re-selecting something
- * @throws IllegalArgumentException
- * if given itemId does not exist in the container of
- * Grid
- */
- boolean select(Object itemId)
- throws IllegalStateException, IllegalArgumentException;
-
- /**
- * Gets the item id of the currently selected item.
- *
- * @return the item id of the currently selected item, or
- * <code>null</code> if nothing is selected
- */
- Object getSelectedRow();
-
- /**
- * Sets whether it's allowed to deselect the selected row through
- * the UI. Deselection is allowed by default.
- *
- * @param deselectAllowed
- * <code>true</code> if the selected row can be
- * deselected without selecting another row instead;
- * otherwise <code>false</code>.
- */
- public void setDeselectAllowed(boolean deselectAllowed);
-
- /**
- * Sets whether it's allowed to deselect the selected row through
- * the UI.
- *
- * @return <code>true</code> if deselection is allowed; otherwise
- * <code>false</code>
- */
- public boolean isDeselectAllowed();
- }
-
- /**
- * A SelectionModel that does not allow for rows to be selected.
- * <p>
- * This interface has a contract of having the same behavior, no matter
- * how the selection model is interacted with. In other words, if the
- * developer is unable to select something programmatically, it is not
- * allowed for the end-user to select anything, either.
- */
- public interface None extends SelectionModel {
-
- /**
- * {@inheritDoc}
- *
- * @return always <code>false</code>.
- */
- @Override
- public boolean isSelected(Object itemId);
-
- /**
- * {@inheritDoc}
- *
- * @return always an empty collection.
- */
- @Override
- public Collection<Object> getSelectedRows();
- }
- }
-
- /**
- * A base class for SelectionModels that contains some of the logic that is
- * reusable.
- */
- public static abstract class AbstractSelectionModel extends
- AbstractGridExtension implements SelectionModel, DataGenerator {
- protected final LinkedHashSet<Object> selection = new LinkedHashSet<>();
-
- @Override
- public boolean isSelected(final Object itemId) {
- return selection.contains(itemId);
- }
-
- @Override
- public Collection<Object> getSelectedRows() {
- return new ArrayList<>(selection);
- }
-
- @Override
- public void setGrid(final LegacyGrid grid) {
- if (grid != null) {
- extend(grid);
- }
- }
-
- /**
- * Sanity check for existence of item id.
- *
- * @param itemId
- * item id to be selected / deselected
- *
- * @throws IllegalArgumentException
- * if item Id doesn't exist in the container of Grid
- */
- protected void checkItemIdExists(Object itemId)
- throws IllegalArgumentException {
- if (!getParentGrid().getContainerDataSource().containsId(itemId)) {
- throw new IllegalArgumentException("Given item id (" + itemId
- + ") does not exist in the container");
- }
- }
-
- /**
- * Sanity check for existence of item ids in given collection.
- *
- * @param itemIds
- * item id collection to be selected / deselected
- *
- * @throws IllegalArgumentException
- * if at least one item id doesn't exist in the container of
- * Grid
- */
- protected void checkItemIdsExist(Collection<?> itemIds)
- throws IllegalArgumentException {
- for (Object itemId : itemIds) {
- checkItemIdExists(itemId);
- }
- }
-
- /**
- * Fires a {@link SelectionEvent} to all the {@link SelectionListener
- * SelectionListeners} currently added to the Grid in which this
- * SelectionModel is.
- * <p>
- * Note that this is only a helper method, and routes the call all the
- * way to Grid. A {@link SelectionModel} is not a
- * {@link SelectionNotifier}
- *
- * @param oldSelection
- * the complete {@link Collection} of the itemIds that were
- * selected <em>before</em> this event happened
- * @param newSelection
- * the complete {@link Collection} of the itemIds that are
- * selected <em>after</em> this event happened
- */
- protected void fireSelectionEvent(final Collection<Object> oldSelection,
- final Collection<Object> newSelection) {
- getParentGrid().fireSelectionEvent(oldSelection, newSelection);
- }
-
- @Override
- public void generateData(Object itemId, Item item, JsonObject rowData) {
- if (isSelected(itemId)) {
- rowData.put(GridState.JSONKEY_SELECTED, true);
- }
- }
-
- @Override
- public void destroyData(Object itemId) {
- // NO-OP
- }
-
- @Override
- protected Object getItemId(String rowKey) {
- return rowKey != null ? super.getItemId(rowKey) : null;
- }
- }
-
- /**
- * A default implementation of a {@link SelectionModel.Single}
- */
- public static class SingleSelectionModel extends AbstractSelectionModel
- implements SelectionModel.Single {
-
- @Override
- protected void extend(AbstractClientConnector target) {
- super.extend(target);
- registerRpc(new SingleSelectionModelServerRpc() {
-
- @Override
- public void select(String rowKey) {
- SingleSelectionModel.this.select(getItemId(rowKey), false);
- }
- });
- }
-
- @Override
- public boolean select(final Object itemId) {
- return select(itemId, true);
- }
-
- protected boolean select(final Object itemId, boolean refresh) {
- if (itemId == null) {
- return deselect(getSelectedRow());
- }
-
- checkItemIdExists(itemId);
-
- final Object selectedRow = getSelectedRow();
- final boolean modified = selection.add(itemId);
- if (modified) {
- final Collection<Object> deselected;
- if (selectedRow != null) {
- deselectInternal(selectedRow, false, true);
- deselected = Collections.singleton(selectedRow);
- } else {
- deselected = Collections.emptySet();
- }
-
- fireSelectionEvent(deselected, selection);
- }
-
- if (refresh) {
- refreshRow(itemId);
- }
-
- return modified;
- }
-
- private boolean deselect(final Object itemId) {
- return deselectInternal(itemId, true, true);
- }
-
- private boolean deselectInternal(final Object itemId,
- boolean fireEventIfNeeded, boolean refresh) {
- final boolean modified = selection.remove(itemId);
- if (modified) {
- if (refresh) {
- refreshRow(itemId);
- }
- if (fireEventIfNeeded) {
- fireSelectionEvent(Collections.singleton(itemId),
- Collections.emptySet());
- }
- }
- return modified;
- }
-
- @Override
- public Object getSelectedRow() {
- if (selection.isEmpty()) {
- return null;
- } else {
- return selection.iterator().next();
- }
- }
-
- /**
- * Resets the selection state.
- * <p>
- * If an item is selected, it will become deselected.
- */
- @Override
- public void reset() {
- deselect(getSelectedRow());
- }
-
- @Override
- public void setDeselectAllowed(boolean deselectAllowed) {
- getState().deselectAllowed = deselectAllowed;
- }
-
- @Override
- public boolean isDeselectAllowed() {
- return getState().deselectAllowed;
- }
-
- @Override
- protected SingleSelectionModelState getState() {
- return (SingleSelectionModelState) super.getState();
- }
- }
-
- /**
- * A default implementation for a {@link SelectionModel.None}
- */
- public static class NoSelectionModel extends AbstractSelectionModel
- implements SelectionModel.None {
-
- @Override
- public boolean isSelected(final Object itemId) {
- return false;
- }
-
- @Override
- public Collection<Object> getSelectedRows() {
- return Collections.emptyList();
- }
-
- /**
- * Semantically resets the selection model.
- * <p>
- * Effectively a no-op.
- */
- @Override
- public void reset() {
- // NOOP
- }
- }
-
- /**
- * A default implementation of a {@link SelectionModel.Multi}
- */
- public static class MultiSelectionModel extends AbstractSelectionModel
- implements SelectionModel.Multi {
-
- /**
- * The default selection size limit.
- *
- * @see #setSelectionLimit(int)
- */
- public static final int DEFAULT_MAX_SELECTIONS = 1000;
-
- private int selectionLimit = DEFAULT_MAX_SELECTIONS;
-
- @Override
- protected void extend(AbstractClientConnector target) {
- super.extend(target);
- registerRpc(new MultiSelectionModelServerRpc() {
-
- @Override
- public void select(List<String> rowKeys) {
- List<Object> items = new ArrayList<>();
- for (String rowKey : rowKeys) {
- items.add(getItemId(rowKey));
- }
- MultiSelectionModel.this.select(items, false);
- }
-
- @Override
- public void deselect(List<String> rowKeys) {
- List<Object> items = new ArrayList<>();
- for (String rowKey : rowKeys) {
- items.add(getItemId(rowKey));
- }
- MultiSelectionModel.this.deselect(items, false);
- }
-
- @Override
- public void selectAll() {
- MultiSelectionModel.this.selectAll(false);
- }
-
- @Override
- public void deselectAll() {
- MultiSelectionModel.this.deselectAll(false);
- }
- });
- }
-
- @Override
- public boolean select(final Object... itemIds)
- throws IllegalArgumentException {
- if (itemIds != null) {
- // select will fire the event
- return select(Arrays.asList(itemIds));
- } else {
- throw new IllegalArgumentException(
- "Vararg array of itemIds may not be null");
- }
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * All items might not be selected if the limit set using
- * {@link #setSelectionLimit(int)} is exceeded.
- */
- @Override
- public boolean select(final Collection<?> itemIds)
- throws IllegalArgumentException {
- return select(itemIds, true);
- }
-
- protected boolean select(final Collection<?> itemIds, boolean refresh) {
- if (itemIds == null) {
- throw new IllegalArgumentException("itemIds may not be null");
- }
-
- // Sanity check
- checkItemIdsExist(itemIds);
-
- final boolean selectionWillChange = !selection.containsAll(itemIds)
- && selection.size() < selectionLimit;
- if (selectionWillChange) {
- final HashSet<Object> oldSelection = new HashSet<>(selection);
- if (selection.size() + itemIds.size() >= selectionLimit) {
- // Add one at a time if there's a risk of overflow
- Iterator<?> iterator = itemIds.iterator();
- while (iterator.hasNext()
- && selection.size() < selectionLimit) {
- selection.add(iterator.next());
- }
- } else {
- selection.addAll(itemIds);
- }
- fireSelectionEvent(oldSelection, selection);
- }
-
- updateAllSelectedState();
-
- if (refresh) {
- for (Object itemId : itemIds) {
- refreshRow(itemId);
- }
- }
-
- return selectionWillChange;
- }
-
- /**
- * Sets the maximum number of rows that can be selected at once. This is
- * a mechanism to prevent exhausting server memory in situations where
- * users select lots of rows. If the limit is reached, newly selected
- * rows will not become recorded.
- * <p>
- * Old selections are not discarded if the current number of selected
- * row exceeds the new limit.
- * <p>
- * The default limit is {@value #DEFAULT_MAX_SELECTIONS} rows.
- *
- * @param selectionLimit
- * the non-negative selection limit to set
- * @throws IllegalArgumentException
- * if the limit is negative
- */
- public void setSelectionLimit(int selectionLimit) {
- if (selectionLimit < 0) {
- throw new IllegalArgumentException(
- "The selection limit must be non-negative");
- }
- this.selectionLimit = selectionLimit;
- }
-
- /**
- * Gets the selection limit.
- *
- * @see #setSelectionLimit(int)
- *
- * @return the selection limit
- */
- public int getSelectionLimit() {
- return selectionLimit;
- }
-
- @Override
- public boolean deselect(final Object... itemIds)
- throws IllegalArgumentException {
- if (itemIds != null) {
- // deselect will fire the event
- return deselect(Arrays.asList(itemIds));
- } else {
- throw new IllegalArgumentException(
- "Vararg array of itemIds may not be null");
- }
- }
-
- @Override
- public boolean deselect(final Collection<?> itemIds)
- throws IllegalArgumentException {
- return deselect(itemIds, true);
- }
-
- protected boolean deselect(final Collection<?> itemIds,
- boolean refresh) {
- if (itemIds == null) {
- throw new IllegalArgumentException("itemIds may not be null");
- }
-
- final boolean hasCommonElements = !Collections.disjoint(itemIds,
- selection);
- if (hasCommonElements) {
- final HashSet<Object> oldSelection = new HashSet<>(selection);
- selection.removeAll(itemIds);
- fireSelectionEvent(oldSelection, selection);
- }
-
- updateAllSelectedState();
-
- if (refresh) {
- for (Object itemId : itemIds) {
- refreshRow(itemId);
- }
- }
-
- return hasCommonElements;
- }
-
- @Override
- public boolean selectAll() {
- return selectAll(true);
- }
-
- protected boolean selectAll(boolean refresh) {
- // select will fire the event
- final Indexed container = getParentGrid().getContainerDataSource();
- if (container != null) {
- return select(container.getItemIds(), refresh);
- } else if (selection.isEmpty()) {
- return false;
- } else {
- /*
- * this should never happen (no container but has a selection),
- * but I guess the only theoretically correct course of
- * action...
- */
- return deselectAll(false);
- }
- }
-
- @Override
- public boolean deselectAll() {
- return deselectAll(true);
- }
-
- protected boolean deselectAll(boolean refresh) {
- // deselect will fire the event
- return deselect(getSelectedRows(), refresh);
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * The returned Collection is in <strong>order of selection</strong>
- * &ndash; the item that was first selected will be first in the
- * collection, and so on. Should an item have been selected twice
- * without being deselected in between, it will have remained in its
- * original position.
- */
- @Override
- public Collection<Object> getSelectedRows() {
- // overridden only for JavaDoc
- return super.getSelectedRows();
- }
-
- /**
- * Resets the selection model.
- * <p>
- * Equivalent to calling {@link #deselectAll()}
- */
- @Override
- public void reset() {
- deselectAll();
- }
-
- @Override
- public boolean setSelected(Collection<?> itemIds)
- throws IllegalArgumentException {
- if (itemIds == null) {
- throw new IllegalArgumentException("itemIds may not be null");
- }
-
- checkItemIdsExist(itemIds);
-
- boolean changed = false;
- Set<Object> selectedRows = new HashSet<>(itemIds);
- final Collection<Object> oldSelection = getSelectedRows();
- Set<Object> added = getDifference(selectedRows, selection);
- if (!added.isEmpty()) {
- changed = true;
- selection.addAll(added);
- for (Object id : added) {
- refreshRow(id);
- }
- }
-
- Set<Object> removed = getDifference(selection, selectedRows);
- if (!removed.isEmpty()) {
- changed = true;
- selection.removeAll(removed);
- for (Object id : removed) {
- refreshRow(id);
- }
- }
-
- if (changed) {
- fireSelectionEvent(oldSelection, selection);
- }
-
- updateAllSelectedState();
-
- return changed;
- }
-
- /**
- * Compares two sets and returns a set containing all values that are
- * present in the first, but not in the second.
- *
- * @param set1
- * first item set
- * @param set2
- * second item set
- * @return all values from set1 which are not present in set2
- */
- private static Set<Object> getDifference(Set<Object> set1,
- Set<Object> set2) {
- Set<Object> diff = new HashSet<>(set1);
- diff.removeAll(set2);
- return diff;
- }
-
- @Override
- public boolean setSelected(Object... itemIds)
- throws IllegalArgumentException {
- if (itemIds != null) {
- return setSelected(Arrays.asList(itemIds));
- } else {
- throw new IllegalArgumentException(
- "Vararg array of itemIds may not be null");
- }
- }
-
- private void updateAllSelectedState() {
- int totalRowCount = getParentGrid().datasource.size();
- int rows = Math.min(totalRowCount, selectionLimit);
- if (getState().allSelected != selection.size() >= rows) {
- getState().allSelected = selection.size() >= rows;
- }
- }
-
- @Override
- protected MultiSelectionModelState getState() {
- return (MultiSelectionModelState) super.getState();
- }
- }
-
- /**
- * A data class which contains information which identifies a row in a
- * {@link LegacyGrid}.
- * <p>
- * Since this class follows the <code>Flyweight</code>-pattern any instance
- * of this object is subject to change without the user knowing it and so
- * should not be stored anywhere outside of the method providing these
- * instances.
- */
- public static class RowReference implements Serializable {
- private final LegacyGrid grid;
-
- private Object itemId;
-
- /**
- * Creates a new row reference for the given grid.
- *
- * @param grid
- * the grid that the row belongs to
- */
- public RowReference(LegacyGrid grid) {
- this.grid = grid;
- }
-
- /**
- * Sets the identifying information for this row
- *
- * @param itemId
- * the item id of the row
- */
- public void set(Object itemId) {
- this.itemId = itemId;
- }
-
- /**
- * Gets the grid that contains the referenced row.
- *
- * @return the grid that contains referenced row
- */
- public LegacyGrid getGrid() {
- return grid;
- }
-
- /**
- * Gets the item id of the row.
- *
- * @return the item id of the row
- */
- public Object getItemId() {
- return itemId;
- }
-
- /**
- * Gets the item for the row.
- *
- * @return the item for the row
- */
- public Item getItem() {
- return grid.getContainerDataSource().getItem(itemId);
- }
- }
-
- /**
- * A data class which contains information which identifies a cell in a
- * {@link LegacyGrid}.
- * <p>
- * Since this class follows the <code>Flyweight</code>-pattern any instance
- * of this object is subject to change without the user knowing it and so
- * should not be stored anywhere outside of the method providing these
- * instances.
- */
- public static class CellReference implements Serializable {
- private final RowReference rowReference;
-
- private Object propertyId;
-
- public CellReference(RowReference rowReference) {
- this.rowReference = rowReference;
- }
-
- /**
- * Sets the identifying information for this cell
- *
- * @param propertyId
- * the property id of the column
- */
- public void set(Object propertyId) {
- this.propertyId = propertyId;
- }
-
- /**
- * Gets the grid that contains the referenced cell.
- *
- * @return the grid that contains referenced cell
- */
- public LegacyGrid getGrid() {
- return rowReference.getGrid();
- }
-
- /**
- * @return the property id of the column
- */
- public Object getPropertyId() {
- return propertyId;
- }
-
- /**
- * @return the property for the cell
- */
- public Property<?> getProperty() {
- return getItem().getItemProperty(propertyId);
- }
-
- /**
- * Gets the item id of the row of the cell.
- *
- * @return the item id of the row
- */
- public Object getItemId() {
- return rowReference.getItemId();
- }
-
- /**
- * Gets the item for the row of the cell.
- *
- * @return the item for the row
- */
- public Item getItem() {
- return rowReference.getItem();
- }
-
- /**
- * Gets the value of the cell.
- *
- * @return the value of the cell
- */
- public Object getValue() {
- return getProperty().getValue();
- }
- }
-
- /**
- * A callback interface for generating custom style names for Grid rows.
- *
- * @see LegacyGrid#setRowStyleGenerator(RowStyleGenerator)
- */
- public interface RowStyleGenerator extends Serializable {
-
- /**
- * Called by Grid to generate a style name for a row.
- *
- * @param row
- * the row to generate a style for
- * @return the style name to add to this row, or {@code null} to not set
- * any style
- */
- public String getStyle(RowReference row);
- }
-
- /**
- * A callback interface for generating custom style names for Grid cells.
- *
- * @see LegacyGrid#setCellStyleGenerator(CellStyleGenerator)
- */
- public interface CellStyleGenerator extends Serializable {
-
- /**
- * Called by Grid to generate a style name for a column.
- *
- * @param cell
- * the cell to generate a style for
- * @return the style name to add to this cell, or {@code null} to not
- * set any style
- */
- public String getStyle(CellReference cell);
- }
-
- /**
- * A callback interface for generating optional descriptions (tooltips) for
- * Grid rows. If a description is generated for a row, it is used for all
- * the cells in the row for which a {@link CellDescriptionGenerator cell
- * description} is not generated.
- *
- * @see LegacyGrid#setRowDescriptionGenerator
- *
- * @since 7.6
- */
- public interface RowDescriptionGenerator extends Serializable {
-
- /**
- * Called by Grid to generate a description (tooltip) for a row. The
- * description may contain HTML which is rendered directly; if this is
- * not desired the returned string must be escaped by the implementing
- * method.
- *
- * @param row
- * the row to generate a description for
- * @return the row description or {@code null} for no description
- */
- public String getDescription(RowReference row);
- }
-
- /**
- * A callback interface for generating optional descriptions (tooltips) for
- * Grid cells. If a cell has both a {@link RowDescriptionGenerator row
- * description} and a cell description, the latter has precedence.
- *
- * @see LegacyGrid#setCellDescriptionGenerator(CellDescriptionGenerator)
- *
- * @since 7.6
- */
- public interface CellDescriptionGenerator extends Serializable {
-
- /**
- * Called by Grid to generate a description (tooltip) for a cell. The
- * description may contain HTML which is rendered directly; if this is
- * not desired the returned string must be escaped by the implementing
- * method.
- *
- * @param cell
- * the cell to generate a description for
- * @return the cell description or {@code null} for no description
- */
- public String getDescription(CellReference cell);
- }
-
- /**
- * Class for generating all row and cell related data for the essential
- * parts of Grid.
- */
- private class RowDataGenerator implements DataGenerator {
-
- private void put(String key, String value, JsonObject object) {
- if (value != null && !value.isEmpty()) {
- object.put(key, value);
- }
- }
-
- @Override
- public void generateData(Object itemId, Item item, JsonObject rowData) {
- RowReference row = new RowReference(LegacyGrid.this);
- row.set(itemId);
-
- if (rowStyleGenerator != null) {
- String style = rowStyleGenerator.getStyle(row);
- put(GridState.JSONKEY_ROWSTYLE, style, rowData);
- }
-
- if (rowDescriptionGenerator != null) {
- String description = rowDescriptionGenerator
- .getDescription(row);
- put(GridState.JSONKEY_ROWDESCRIPTION, description, rowData);
-
- }
-
- JsonObject cellStyles = Json.createObject();
- JsonObject cellData = Json.createObject();
- JsonObject cellDescriptions = Json.createObject();
-
- CellReference cell = new CellReference(row);
-
- for (Column column : getColumns()) {
- cell.set(column.getPropertyId());
-
- writeData(cell, cellData);
- writeStyles(cell, cellStyles);
- writeDescriptions(cell, cellDescriptions);
- }
-
- if (cellDescriptionGenerator != null
- && cellDescriptions.keys().length > 0) {
- rowData.put(GridState.JSONKEY_CELLDESCRIPTION,
- cellDescriptions);
- }
-
- if (cellStyleGenerator != null && cellStyles.keys().length > 0) {
- rowData.put(GridState.JSONKEY_CELLSTYLES, cellStyles);
- }
-
- rowData.put(GridState.JSONKEY_DATA, cellData);
- }
-
- private void writeStyles(CellReference cell, JsonObject styles) {
- if (cellStyleGenerator != null) {
- String style = cellStyleGenerator.getStyle(cell);
- put(columnKeys.key(cell.getPropertyId()), style, styles);
- }
- }
-
- private void writeDescriptions(CellReference cell,
- JsonObject descriptions) {
- if (cellDescriptionGenerator != null) {
- String description = cellDescriptionGenerator
- .getDescription(cell);
- put(columnKeys.key(cell.getPropertyId()), description,
- descriptions);
- }
- }
-
- private void writeData(CellReference cell, JsonObject data) {
- Column column = getColumn(cell.getPropertyId());
- LegacyConverter<?, ?> converter = column.getConverter();
- Renderer<?> renderer = column.getRenderer();
-
- Item item = cell.getItem();
- Object modelValue = item.getItemProperty(cell.getPropertyId())
- .getValue();
-
- data.put(columnKeys.key(cell.getPropertyId()), AbstractRenderer
- .encodeValue(modelValue, renderer, converter, getLocale()));
- }
-
- @Override
- public void destroyData(Object itemId) {
- // NO-OP
- }
- }
-
- /**
- * Abstract base class for Grid header and footer sections.
- *
- * @since 7.6
- * @param <ROWTYPE>
- * the type of the rows in the section
- */
- public abstract static class StaticSection<ROWTYPE extends StaticSection.StaticRow<?>>
- implements Serializable {
-
- /**
- * Abstract base class for Grid header and footer rows.
- *
- * @param <CELLTYPE>
- * the type of the cells in the row
- */
- public abstract static class StaticRow<CELLTYPE extends StaticCell>
- implements Serializable {
-
- private RowState rowState = new RowState();
- protected StaticSection<?> section;
- private Map<Object, CELLTYPE> cells = new LinkedHashMap<>();
- private Map<Set<CELLTYPE>, CELLTYPE> cellGroups = new HashMap<>();
-
- protected StaticRow(StaticSection<?> section) {
- this.section = section;
- }
-
- protected void addCell(Object propertyId) {
- CELLTYPE cell = createCell();
- cell.setColumnId(
- section.grid.getColumn(propertyId).getState().id);
- cells.put(propertyId, cell);
- rowState.cells.add(cell.getCellState());
- }
-
- protected void removeCell(Object propertyId) {
- CELLTYPE cell = cells.remove(propertyId);
- if (cell != null) {
- Set<CELLTYPE> cellGroupForCell = getCellGroupForCell(cell);
- if (cellGroupForCell != null) {
- removeCellFromGroup(cell, cellGroupForCell);
- }
- rowState.cells.remove(cell.getCellState());
- }
- }
-
- private void removeCellFromGroup(CELLTYPE cell,
- Set<CELLTYPE> cellGroup) {
- String columnId = cell.getColumnId();
- for (Set<String> group : rowState.cellGroups.keySet()) {
- if (group.contains(columnId)) {
- if (group.size() > 2) {
- // Update map key correctly
- CELLTYPE mergedCell = cellGroups.remove(cellGroup);
- cellGroup.remove(cell);
- cellGroups.put(cellGroup, mergedCell);
-
- group.remove(columnId);
- } else {
- rowState.cellGroups.remove(group);
- cellGroups.remove(cellGroup);
- }
- return;
- }
- }
- }
-
- /**
- * Creates and returns a new instance of the cell type.
- *
- * @return the created cell
- */
- protected abstract CELLTYPE createCell();
-
- protected RowState getRowState() {
- return rowState;
- }
-
- /**
- * Returns the cell for the given property id on this row. If the
- * column is merged returned cell is the cell for the whole group.
- *
- * @param propertyId
- * the property id of the column
- * @return the cell for the given property, merged cell for merged
- * properties, null if not found
- */
- public CELLTYPE getCell(Object propertyId) {
- CELLTYPE cell = cells.get(propertyId);
- Set<CELLTYPE> cellGroup = getCellGroupForCell(cell);
- if (cellGroup != null) {
- cell = cellGroups.get(cellGroup);
- }
- return cell;
- }
-
- /**
- * Merges columns cells in a row
- *
- * @param propertyIds
- * The property ids of columns to merge
- * @return The remaining visible cell after the merge
- */
- public CELLTYPE join(Object... propertyIds) {
- assert propertyIds.length > 1 : "You need to merge at least 2 properties";
-
- Set<CELLTYPE> cells = new HashSet<>();
- for (int i = 0; i < propertyIds.length; ++i) {
- cells.add(getCell(propertyIds[i]));
- }
-
- return join(cells);
- }
-
- /**
- * Merges columns cells in a row
- *
- * @param cells
- * The cells to merge. Must be from the same row.
- * @return The remaining visible cell after the merge
- */
- public CELLTYPE join(CELLTYPE... cells) {
- assert cells.length > 1 : "You need to merge at least 2 cells";
-
- return join(new HashSet<>(Arrays.asList(cells)));
- }
-
- protected CELLTYPE join(Set<CELLTYPE> cells) {
- for (CELLTYPE cell : cells) {
- if (getCellGroupForCell(cell) != null) {
- throw new IllegalArgumentException(
- "Cell already merged");
- } else if (!this.cells.containsValue(cell)) {
- throw new IllegalArgumentException(
- "Cell does not exist on this row");
- }
- }
-
- // Create new cell data for the group
- CELLTYPE newCell = createCell();
-
- Set<String> columnGroup = new HashSet<>();
- for (CELLTYPE cell : cells) {
- columnGroup.add(cell.getColumnId());
- }
- rowState.cellGroups.put(columnGroup, newCell.getCellState());
- cellGroups.put(cells, newCell);
- return newCell;
- }
-
- private Set<CELLTYPE> getCellGroupForCell(CELLTYPE cell) {
- for (Set<CELLTYPE> group : cellGroups.keySet()) {
- if (group.contains(cell)) {
- return group;
- }
- }
- return null;
- }
-
- /**
- * Returns the custom style name for this row.
- *
- * @return the style name or null if no style name has been set
- */
- public String getStyleName() {
- return getRowState().styleName;
- }
-
- /**
- * Sets a custom style name for this row.
- *
- * @param styleName
- * the style name to set or null to not use any style
- * name
- */
- public void setStyleName(String styleName) {
- getRowState().styleName = styleName;
- }
-
- /**
- * Writes the declarative design to the given table row element.
- *
- * @since 7.5.0
- * @param trElement
- * Element to write design to
- * @param designContext
- * the design context
- */
- protected void writeDesign(Element trElement,
- DesignContext designContext) {
- Set<CELLTYPE> visited = new HashSet<>();
- for (LegacyGrid.Column column : section.grid.getColumns()) {
- CELLTYPE cell = getCell(column.getPropertyId());
- if (visited.contains(cell)) {
- continue;
- }
- visited.add(cell);
-
- Element cellElement = trElement
- .appendElement(getCellTagName());
- cell.writeDesign(cellElement, designContext);
-
- for (Entry<Set<CELLTYPE>, CELLTYPE> entry : cellGroups
- .entrySet()) {
- if (entry.getValue() == cell) {
- cellElement.attr("colspan",
- "" + entry.getKey().size());
- break;
- }
- }
- }
- }
-
- /**
- * Reads the declarative design from the given table row element.
- *
- * @since 7.5.0
- * @param trElement
- * Element to read design from
- * @param designContext
- * the design context
- * @throws DesignException
- * if the given table row contains unexpected children
- */
- protected void readDesign(Element trElement,
- DesignContext designContext) throws DesignException {
- Elements cellElements = trElement.children();
- int totalColSpans = 0;
- for (int i = 0; i < cellElements.size(); ++i) {
- Element element = cellElements.get(i);
- if (!element.tagName().equals(getCellTagName())) {
- throw new DesignException(
- "Unexpected element in tr while expecting "
- + getCellTagName() + ": "
- + element.tagName());
- }
-
- int columnIndex = i + totalColSpans;
-
- int colspan = DesignAttributeHandler.readAttribute(
- "colspan", element.attributes(), 1, int.class);
-
- Set<CELLTYPE> cells = new HashSet<>();
- for (int c = 0; c < colspan; ++c) {
- cells.add(getCell(section.grid.getColumns()
- .get(columnIndex + c).getPropertyId()));
- }
-
- if (colspan > 1) {
- totalColSpans += colspan - 1;
- join(cells).readDesign(element, designContext);
- } else {
- cells.iterator().next().readDesign(element,
- designContext);
- }
- }
- }
-
- abstract protected String getCellTagName();
-
- void detach() {
- for (CELLTYPE cell : cells.values()) {
- cell.detach();
- }
- }
- }
-
- /**
- * A header or footer cell. Has a simple textual caption.
- */
- abstract static class StaticCell implements Serializable {
-
- private CellState cellState = new CellState();
- private StaticRow<?> row;
-
- protected StaticCell(StaticRow<?> row) {
- this.row = row;
- }
-
- void setColumnId(String id) {
- cellState.columnId = id;
- }
-
- String getColumnId() {
- return cellState.columnId;
- }
-
- /**
- * Gets the row where this cell is.
- *
- * @return row for this cell
- */
- public StaticRow<?> getRow() {
- return row;
- }
-
- protected CellState getCellState() {
- return cellState;
- }
-
- /**
- * Sets the text displayed in this cell.
- *
- * @param text
- * a plain text caption
- */
- public void setText(String text) {
- removeComponentIfPresent();
- cellState.text = text;
- cellState.type = GridStaticCellType.TEXT;
- row.section.markAsDirty();
- }
-
- /**
- * Returns the text displayed in this cell.
- *
- * @return the plain text caption
- */
- public String getText() {
- if (cellState.type != GridStaticCellType.TEXT) {
- throw new IllegalStateException(
- "Cannot fetch Text from a cell with type "
- + cellState.type);
- }
- return cellState.text;
- }
-
- /**
- * Returns the HTML content displayed in this cell.
- *
- * @return the html
- *
- */
- public String getHtml() {
- if (cellState.type != GridStaticCellType.HTML) {
- throw new IllegalStateException(
- "Cannot fetch HTML from a cell with type "
- + cellState.type);
- }
- return cellState.html;
- }
-
- /**
- * Sets the HTML content displayed in this cell.
- *
- * @param html
- * the html to set
- */
- public void setHtml(String html) {
- removeComponentIfPresent();
- cellState.html = html;
- cellState.type = GridStaticCellType.HTML;
- row.section.markAsDirty();
- }
-
- /**
- * Returns the component displayed in this cell.
- *
- * @return the component
- */
- public Component getComponent() {
- if (cellState.type != GridStaticCellType.WIDGET) {
- throw new IllegalStateException(
- "Cannot fetch Component from a cell with type "
- + cellState.type);
- }
- return (Component) cellState.connector;
- }
-
- /**
- * Sets the component displayed in this cell.
- *
- * @param component
- * the component to set
- */
- public void setComponent(Component component) {
- removeComponentIfPresent();
- component.setParent(row.section.grid);
- cellState.connector = component;
- cellState.type = GridStaticCellType.WIDGET;
- row.section.markAsDirty();
- }
-
- /**
- * Returns the type of content stored in this cell.
- *
- * @return cell content type
- */
- public GridStaticCellType getCellType() {
- return cellState.type;
- }
-
- /**
- * Returns the custom style name for this cell.
- *
- * @return the style name or null if no style name has been set
- */
- public String getStyleName() {
- return cellState.styleName;
- }
-
- /**
- * Sets a custom style name for this cell.
- *
- * @param styleName
- * the style name to set or null to not use any style
- * name
- */
- public void setStyleName(String styleName) {
- cellState.styleName = styleName;
- row.section.markAsDirty();
- }
-
- private void removeComponentIfPresent() {
- Component component = (Component) cellState.connector;
- if (component != null) {
- component.setParent(null);
- cellState.connector = null;
- }
- }
-
- /**
- * Writes the declarative design to the given table cell element.
- *
- * @since 7.5.0
- * @param cellElement
- * Element to write design to
- * @param designContext
- * the design context
- */
- protected void writeDesign(Element cellElement,
- DesignContext designContext) {
- switch (cellState.type) {
- case TEXT:
- cellElement.attr("plain-text", true);
- cellElement.appendText(getText());
- break;
- case HTML:
- cellElement.append(getHtml());
- break;
- case WIDGET:
- cellElement.appendChild(
- designContext.createElement(getComponent()));
- break;
- }
- }
-
- /**
- * Reads the declarative design from the given table cell element.
- *
- * @since 7.5.0
- * @param cellElement
- * Element to read design from
- * @param designContext
- * the design context
- */
- protected void readDesign(Element cellElement,
- DesignContext designContext) {
- if (!cellElement.hasAttr("plain-text")) {
- if (cellElement.children().size() > 0
- && cellElement.child(0).tagName().contains("-")) {
- setComponent(
- designContext.readDesign(cellElement.child(0)));
- } else {
- setHtml(cellElement.html());
- }
- } else {
- // text – need to unescape HTML entities
- setText(DesignFormatter
- .decodeFromTextNode(cellElement.html()));
- }
- }
-
- void detach() {
- removeComponentIfPresent();
- }
- }
-
- protected LegacyGrid grid;
- protected List<ROWTYPE> rows = new ArrayList<>();
-
- /**
- * Sets the visibility of the whole section.
- *
- * @param visible
- * true to show this section, false to hide
- */
- public void setVisible(boolean visible) {
- if (getSectionState().visible != visible) {
- getSectionState().visible = visible;
- markAsDirty();
- }
- }
-
- /**
- * Returns the visibility of this section.
- *
- * @return true if visible, false otherwise.
- */
- public boolean isVisible() {
- return getSectionState().visible;
- }
-
- /**
- * Removes the row at the given position.
- *
- * @param rowIndex
- * the position of the row
- *
- * @throws IllegalArgumentException
- * if no row exists at given index
- * @see #removeRow(StaticRow)
- * @see #addRowAt(int)
- * @see #appendRow()
- * @see #prependRow()
- */
- public ROWTYPE removeRow(int rowIndex) {
- if (rowIndex >= rows.size() || rowIndex < 0) {
- throw new IllegalArgumentException(
- "No row at given index " + rowIndex);
- }
- ROWTYPE row = rows.remove(rowIndex);
- row.detach();
- getSectionState().rows.remove(rowIndex);
-
- markAsDirty();
- return row;
- }
-
- /**
- * Removes the given row from the section.
- *
- * @param row
- * the row to be removed
- *
- * @throws IllegalArgumentException
- * if the row does not exist in this section
- * @see #removeRow(int)
- * @see #addRowAt(int)
- * @see #appendRow()
- * @see #prependRow()
- */
- public void removeRow(ROWTYPE row) {
- try {
- removeRow(rows.indexOf(row));
- } catch (IndexOutOfBoundsException e) {
- throw new IllegalArgumentException(
- "Section does not contain the given row");
- }
- }
-
- /**
- * Gets row at given index.
- *
- * @param rowIndex
- * 0 based index for row. Counted from top to bottom
- * @return row at given index
- */
- public ROWTYPE getRow(int rowIndex) {
- if (rowIndex >= rows.size() || rowIndex < 0) {
- throw new IllegalArgumentException(
- "No row at given index " + rowIndex);
- }
- return rows.get(rowIndex);
- }
-
- /**
- * Adds a new row at the top of this section.
- *
- * @return the new row
- * @see #appendRow()
- * @see #addRowAt(int)
- * @see #removeRow(StaticRow)
- * @see #removeRow(int)
- */
- public ROWTYPE prependRow() {
- return addRowAt(0);
- }
-
- /**
- * Adds a new row at the bottom of this section.
- *
- * @return the new row
- * @see #prependRow()
- * @see #addRowAt(int)
- * @see #removeRow(StaticRow)
- * @see #removeRow(int)
- */
- public ROWTYPE appendRow() {
- return addRowAt(rows.size());
- }
-
- /**
- * Inserts a new row at the given position.
- *
- * @param index
- * the position at which to insert the row
- * @return the new row
- *
- * @throws IndexOutOfBoundsException
- * if the index is out of bounds
- * @see #appendRow()
- * @see #prependRow()
- * @see #removeRow(StaticRow)
- * @see #removeRow(int)
- */
- public ROWTYPE addRowAt(int index) {
- if (index > rows.size() || index < 0) {
- throw new IllegalArgumentException(
- "Unable to add row at index " + index);
- }
- ROWTYPE row = createRow();
- rows.add(index, row);
- getSectionState().rows.add(index, row.getRowState());
-
- for (Object id : grid.columns.keySet()) {
- row.addCell(id);
- }
-
- markAsDirty();
- return row;
- }
-
- /**
- * Gets the amount of rows in this section.
- *
- * @return row count
- */
- public int getRowCount() {
- return rows.size();
- }
-
- protected abstract GridStaticSectionState getSectionState();
-
- protected abstract ROWTYPE createRow();
-
- /**
- * Informs the grid that state has changed and it should be redrawn.
- */
- protected void markAsDirty() {
- grid.markAsDirty();
- }
-
- /**
- * Removes a column for given property id from the section.
- *
- * @param propertyId
- * property to be removed
- */
- protected void removeColumn(Object propertyId) {
- for (ROWTYPE row : rows) {
- row.removeCell(propertyId);
- }
- }
-
- /**
- * Adds a column for given property id to the section.
- *
- * @param propertyId
- * property to be added
- */
- protected void addColumn(Object propertyId) {
- for (ROWTYPE row : rows) {
- row.addCell(propertyId);
- }
- }
-
- /**
- * Performs a sanity check that section is in correct state.
- *
- * @throws IllegalStateException
- * if merged cells are not i n continuous range
- */
- protected void sanityCheck() throws IllegalStateException {
- List<String> columnOrder = grid.getState().columnOrder;
- for (ROWTYPE row : rows) {
- for (Set<String> cellGroup : row.getRowState().cellGroups
- .keySet()) {
- if (!checkCellGroupAndOrder(columnOrder, cellGroup)) {
- throw new IllegalStateException(
- "Not all merged cells were in a continuous range.");
- }
- }
- }
- }
-
- private boolean checkCellGroupAndOrder(List<String> columnOrder,
- Set<String> cellGroup) {
- if (!columnOrder.containsAll(cellGroup)) {
- return false;
- }
-
- for (int i = 0; i < columnOrder.size(); ++i) {
- if (!cellGroup.contains(columnOrder.get(i))) {
- continue;
- }
-
- for (int j = 1; j < cellGroup.size(); ++j) {
- if (!cellGroup.contains(columnOrder.get(i + j))) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
-
- /**
- * Writes the declarative design to the given table section element.
- *
- * @since 7.5.0
- * @param tableSectionElement
- * Element to write design to
- * @param designContext
- * the design context
- */
- protected void writeDesign(Element tableSectionElement,
- DesignContext designContext) {
- for (ROWTYPE row : rows) {
- row.writeDesign(tableSectionElement.appendElement("tr"),
- designContext);
- }
- }
-
- /**
- * Writes the declarative design from the given table section element.
- *
- * @since 7.5.0
- * @param tableSectionElement
- * Element to read design from
- * @param designContext
- * the design context
- * @throws DesignException
- * if the table section contains unexpected children
- */
- protected void readDesign(Element tableSectionElement,
- DesignContext designContext) throws DesignException {
- while (rows.size() > 0) {
- removeRow(0);
- }
-
- for (Element row : tableSectionElement.children()) {
- if (!row.tagName().equals("tr")) {
- throw new DesignException("Unexpected element in "
- + tableSectionElement.tagName() + ": "
- + row.tagName());
- }
- appendRow().readDesign(row, designContext);
- }
- }
- }
-
- /**
- * Represents the header section of a Grid.
- */
- protected static class Header extends StaticSection<HeaderRow> {
-
- private HeaderRow defaultRow = null;
- private final GridStaticSectionState headerState = new GridStaticSectionState();
-
- protected Header(LegacyGrid grid) {
- this.grid = grid;
- grid.getState(true).header = headerState;
- HeaderRow row = createRow();
- rows.add(row);
- setDefaultRow(row);
- getSectionState().rows.add(row.getRowState());
- }
-
- /**
- * Sets the default row of this header. The default row is a special
- * header row providing a user interface for sorting columns.
- *
- * @param row
- * the new default row, or null for no default row
- *
- * @throws IllegalArgumentException
- * this header does not contain the row
- */
- public void setDefaultRow(HeaderRow row) {
- if (row == defaultRow) {
- return;
- }
-
- if (row != null && !rows.contains(row)) {
- throw new IllegalArgumentException(
- "Cannot set a default row that does not exist in the section");
- }
-
- if (defaultRow != null) {
- defaultRow.setDefaultRow(false);
- }
-
- if (row != null) {
- row.setDefaultRow(true);
- }
-
- defaultRow = row;
- markAsDirty();
- }
-
- /**
- * Returns the current default row of this header. The default row is a
- * special header row providing a user interface for sorting columns.
- *
- * @return the default row or null if no default row set
- */
- public HeaderRow getDefaultRow() {
- return defaultRow;
- }
-
- @Override
- protected GridStaticSectionState getSectionState() {
- return headerState;
- }
-
- @Override
- protected HeaderRow createRow() {
- return new HeaderRow(this);
- }
-
- @Override
- public HeaderRow removeRow(int rowIndex) {
- HeaderRow row = super.removeRow(rowIndex);
- if (row == defaultRow) {
- // Default Header Row was just removed.
- setDefaultRow(null);
- }
- return row;
- }
-
- @Override
- protected void sanityCheck() throws IllegalStateException {
- super.sanityCheck();
-
- boolean hasDefaultRow = false;
- for (HeaderRow row : rows) {
- if (row.getRowState().defaultRow) {
- if (!hasDefaultRow) {
- hasDefaultRow = true;
- } else {
- throw new IllegalStateException(
- "Multiple default rows in header");
- }
- }
- }
- }
-
- @Override
- protected void readDesign(Element tableSectionElement,
- DesignContext designContext) {
- super.readDesign(tableSectionElement, designContext);
-
- if (defaultRow == null && !rows.isEmpty()) {
- grid.setDefaultHeaderRow(rows.get(0));
- }
- }
- }
-
- /**
- * Represents a header row in Grid.
- */
- public static class HeaderRow extends StaticSection.StaticRow<HeaderCell> {
-
- protected HeaderRow(StaticSection<?> section) {
- super(section);
- }
-
- private void setDefaultRow(boolean value) {
- getRowState().defaultRow = value;
- }
-
- private boolean isDefaultRow() {
- return getRowState().defaultRow;
- }
-
- @Override
- protected HeaderCell createCell() {
- return new HeaderCell(this);
- }
-
- @Override
- protected String getCellTagName() {
- return "th";
- }
-
- @Override
- protected void writeDesign(Element trElement,
- DesignContext designContext) {
- super.writeDesign(trElement, designContext);
-
- if (section.grid.getDefaultHeaderRow() == this) {
- DesignAttributeHandler.writeAttribute("default",
- trElement.attributes(), true, null, boolean.class);
- }
- }
-
- @Override
- protected void readDesign(Element trElement,
- DesignContext designContext) {
- super.readDesign(trElement, designContext);
-
- boolean defaultRow = DesignAttributeHandler.readAttribute("default",
- trElement.attributes(), false, boolean.class);
- if (defaultRow) {
- section.grid.setDefaultHeaderRow(this);
- }
- }
- }
-
- /**
- * Represents a header cell in Grid. Can be a merged cell for multiple
- * columns.
- */
- public static class HeaderCell extends StaticSection.StaticCell {
-
- protected HeaderCell(HeaderRow row) {
- super(row);
- }
- }
-
- /**
- * Represents the footer section of a Grid. By default Footer is not
- * visible.
- */
- protected static class Footer extends StaticSection<FooterRow> {
-
- private final GridStaticSectionState footerState = new GridStaticSectionState();
-
- protected Footer(LegacyGrid grid) {
- this.grid = grid;
- grid.getState(true).footer = footerState;
- }
-
- @Override
- protected GridStaticSectionState getSectionState() {
- return footerState;
- }
-
- @Override
- protected FooterRow createRow() {
- return new FooterRow(this);
- }
-
- @Override
- protected void sanityCheck() throws IllegalStateException {
- super.sanityCheck();
- }
- }
-
- /**
- * Represents a footer row in Grid.
- */
- public static class FooterRow extends StaticSection.StaticRow<FooterCell> {
-
- protected FooterRow(StaticSection<?> section) {
- super(section);
- }
-
- @Override
- protected FooterCell createCell() {
- return new FooterCell(this);
- }
-
- @Override
- protected String getCellTagName() {
- return "td";
- }
-
- }
-
- /**
- * Represents a footer cell in Grid.
- */
- public static class FooterCell extends StaticSection.StaticCell {
-
- protected FooterCell(FooterRow row) {
- super(row);
- }
- }
-
- /**
- * A column in the grid. Can be obtained by calling
- * {@link LegacyGrid#getColumn(Object propertyId)}.
- */
- public static class Column implements Serializable {
-
- /**
- * The state of the column shared to the client
- */
- private final GridColumnState state;
-
- /**
- * The grid this column is associated with
- */
- private final LegacyGrid grid;
-
- /**
- * Backing property for column
- */
- private final Object propertyId;
-
- private LegacyConverter<?, Object> converter;
-
- /**
- * A check for allowing the
- * {@link #Column(LegacyGrid, GridColumnState, Object) constructor} to call
- * {@link #setConverter(LegacyConverter)} with a <code>null</code>, even
- * if model and renderer aren't compatible.
- */
- private boolean isFirstConverterAssignment = true;
-
- /**
- * Internally used constructor.
- *
- * @param grid
- * The grid this column belongs to. Should not be null.
- * @param state
- * the shared state of this column
- * @param propertyId
- * the backing property id for this column
- */
- Column(LegacyGrid grid, GridColumnState state, Object propertyId) {
- this.grid = grid;
- this.state = state;
- this.propertyId = propertyId;
- internalSetRenderer(new TextRenderer());
- }
-
- /**
- * Returns the serializable state of this column that is sent to the
- * client side connector.
- *
- * @return the internal state of the column
- */
- GridColumnState getState() {
- return state;
- }
-
- /**
- * Returns the property id for the backing property of this Column
- *
- * @return property id
- */
- public Object getPropertyId() {
- return propertyId;
- }
-
- /**
- * Returns the caption of the header. By default the header caption is
- * the property id of the column.
- *
- * @return the text in the default row of header.
- *
- * @throws IllegalStateException
- * if the column no longer is attached to the grid
- */
- public String getHeaderCaption() throws IllegalStateException {
- checkColumnIsAttached();
-
- return state.headerCaption;
- }
-
- /**
- * Sets the caption of the header. This caption is also used as the
- * hiding toggle caption, unless it is explicitly set via
- * {@link #setHidingToggleCaption(String)}.
- *
- * @param caption
- * the text to show in the caption
- * @return the column itself
- *
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public Column setHeaderCaption(String caption)
- throws IllegalStateException {
- checkColumnIsAttached();
- if (caption == null) {
- caption = ""; // Render null as empty
- }
- state.headerCaption = caption;
-
- HeaderRow row = grid.getHeader().getDefaultRow();
- if (row != null) {
- row.getCell(grid.getPropertyIdByColumnId(state.id))
- .setText(caption);
- }
- return this;
- }
-
- /**
- * Gets the caption of the hiding toggle for this column.
- *
- * @since 7.5.0
- * @see #setHidingToggleCaption(String)
- * @return the caption for the hiding toggle for this column
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public String getHidingToggleCaption() throws IllegalStateException {
- checkColumnIsAttached();
- return state.hidingToggleCaption;
- }
-
- /**
- * Sets the caption of the hiding toggle for this column. Shown in the
- * toggle for this column in the grid's sidebar when the column is
- * {@link #isHidable() hidable}.
- * <p>
- * The default value is <code>null</code>, and in that case the column's
- * {@link #getHeaderCaption() header caption} is used.
- * <p>
- * <em>NOTE:</em> setting this to empty string might cause the hiding
- * toggle to not render correctly.
- *
- * @since 7.5.0
- * @param hidingToggleCaption
- * the text to show in the column hiding toggle
- * @return the column itself
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public Column setHidingToggleCaption(String hidingToggleCaption)
- throws IllegalStateException {
- checkColumnIsAttached();
- state.hidingToggleCaption = hidingToggleCaption;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Returns the width (in pixels). By default a column is 100px wide.
- *
- * @return the width in pixels of the column
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public double getWidth() throws IllegalStateException {
- checkColumnIsAttached();
- return state.width;
- }
-
- /**
- * Sets the width (in pixels).
- * <p>
- * This overrides any configuration set by any of
- * {@link #setExpandRatio(int)}, {@link #setMinimumWidth(double)} or
- * {@link #setMaximumWidth(double)}.
- *
- * @param pixelWidth
- * the new pixel width of the column
- * @return the column itself
- *
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- * @throws IllegalArgumentException
- * thrown if pixel width is less than zero
- */
- public Column setWidth(double pixelWidth)
- throws IllegalStateException, IllegalArgumentException {
- checkColumnIsAttached();
- if (pixelWidth < 0) {
- throw new IllegalArgumentException(
- "Pixel width should be greated than 0 (in " + toString()
- + ")");
- }
- if (state.width != pixelWidth) {
- state.width = pixelWidth;
- grid.markAsDirty();
- grid.fireColumnResizeEvent(this, false);
- }
- return this;
- }
-
- /**
- * Returns whether this column has an undefined width.
- *
- * @since 7.6
- * @return whether the width is undefined
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public boolean isWidthUndefined() {
- checkColumnIsAttached();
- return state.width < 0;
- }
-
- /**
- * Marks the column width as undefined. An undefined width means the
- * grid is free to resize the column based on the cell contents and
- * available space in the grid.
- *
- * @return the column itself
- */
- public Column setWidthUndefined() {
- checkColumnIsAttached();
- if (!isWidthUndefined()) {
- state.width = -1;
- grid.markAsDirty();
- grid.fireColumnResizeEvent(this, false);
- }
- return this;
- }
-
- /**
- * Checks if column is attached and throws an
- * {@link IllegalStateException} if it is not
- *
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- protected void checkColumnIsAttached() throws IllegalStateException {
- if (grid.getColumnByColumnId(state.id) == null) {
- throw new IllegalStateException("Column no longer exists.");
- }
- }
-
- /**
- * Sets this column as the last frozen column in its grid.
- *
- * @return the column itself
- *
- * @throws IllegalArgumentException
- * if the column is no longer attached to any grid
- * @see LegacyGrid#setFrozenColumnCount(int)
- */
- public Column setLastFrozenColumn() {
- checkColumnIsAttached();
- grid.setFrozenColumnCount(
- grid.getState(false).columnOrder.indexOf(getState().id)
- + 1);
- return this;
- }
-
- /**
- * Sets the renderer for this column.
- * <p>
- * If a suitable converter isn't defined explicitly, the session
- * converter factory is used to find a compatible converter.
- *
- * @param renderer
- * the renderer to use
- * @return the column itself
- *
- * @throws IllegalArgumentException
- * if no compatible converter could be found
- *
- * @see VaadinSession#getConverterFactory()
- * @see LegacyConverterUtil#getConverter(Class, Class, VaadinSession)
- * @see #setConverter(LegacyConverter)
- */
- public Column setRenderer(Renderer<?> renderer) {
- if (!internalSetRenderer(renderer)) {
- throw new IllegalArgumentException(
- "Could not find a converter for converting from the model type "
- + getModelType()
- + " to the renderer presentation type "
- + renderer.getPresentationType() + " (in "
- + toString() + ")");
- }
- return this;
- }
-
- /**
- * Sets the renderer for this column and the converter used to convert
- * from the property value type to the renderer presentation type.
- *
- * @param renderer
- * the renderer to use, cannot be null
- * @param converter
- * the converter to use
- * @return the column itself
- *
- * @throws IllegalArgumentException
- * if the renderer is already associated with a grid column
- */
- public <T> Column setRenderer(Renderer<T> renderer,
- LegacyConverter<? extends T, ?> converter) {
- if (renderer.getParent() != null) {
- throw new IllegalArgumentException(
- "Cannot set a renderer that is already connected to a grid column (in "
- + toString() + ")");
- }
-
- if (getRenderer() != null) {
- grid.removeExtension(getRenderer());
- }
-
- grid.addRenderer(renderer);
- state.rendererConnector = renderer;
- setConverter(converter);
- return this;
- }
-
- /**
- * Sets the converter used to convert from the property value type to
- * the renderer presentation type.
- *
- * @param converter
- * the converter to use, or {@code null} to not use any
- * converters
- * @return the column itself
- *
- * @throws IllegalArgumentException
- * if the types are not compatible
- */
- public Column setConverter(LegacyConverter<?, ?> converter)
- throws IllegalArgumentException {
- Class<?> modelType = getModelType();
- if (converter != null) {
- if (!converter.getModelType().isAssignableFrom(modelType)) {
- throw new IllegalArgumentException(
- "The converter model type "
- + converter.getModelType()
- + " is not compatible with the property type "
- + modelType + " (in " + toString() + ")");
-
- } else if (!getRenderer().getPresentationType()
- .isAssignableFrom(converter.getPresentationType())) {
- throw new IllegalArgumentException(
- "The converter presentation type "
- + converter.getPresentationType()
- + " is not compatible with the renderer presentation type "
- + getRenderer().getPresentationType()
- + " (in " + toString() + ")");
- }
- }
-
- else {
- /*
- * Since the converter is null (i.e. will be removed), we need
- * to know that the renderer and model are compatible. If not,
- * we can't allow for this to happen.
- *
- * The constructor is allowed to call this method with null
- * without any compatibility checks, therefore we have a special
- * case for it.
- */
-
- Class<?> rendererPresentationType = getRenderer()
- .getPresentationType();
- if (!isFirstConverterAssignment && !rendererPresentationType
- .isAssignableFrom(modelType)) {
- throw new IllegalArgumentException(
- "Cannot remove converter, "
- + "as renderer's presentation type "
- + rendererPresentationType.getName()
- + " and column's " + "model "
- + modelType.getName() + " type aren't "
- + "directly compatible with each other (in "
- + toString() + ")");
- }
- }
-
- isFirstConverterAssignment = false;
-
- @SuppressWarnings("unchecked")
- LegacyConverter<?, Object> castConverter = (LegacyConverter<?, Object>) converter;
- this.converter = castConverter;
-
- return this;
- }
-
- /**
- * Returns the renderer instance used by this column.
- *
- * @return the renderer
- */
- public Renderer<?> getRenderer() {
- return (Renderer<?>) getState().rendererConnector;
- }
-
- /**
- * Returns the converter instance used by this column.
- *
- * @return the converter
- */
- public LegacyConverter<?, ?> getConverter() {
- return converter;
- }
-
- private <T> boolean internalSetRenderer(Renderer<T> renderer) {
-
- LegacyConverter<? extends T, ?> converter;
- if (isCompatibleWithProperty(renderer, getConverter())) {
- // Use the existing converter (possibly none) if types
- // compatible
- converter = (LegacyConverter<? extends T, ?>) getConverter();
- } else {
- converter = LegacyConverterUtil.getConverter(
- renderer.getPresentationType(), getModelType(),
- getSession());
- }
- setRenderer(renderer, converter);
- return isCompatibleWithProperty(renderer, converter);
- }
-
- private VaadinSession getSession() {
- UI ui = grid.getUI();
- return ui != null ? ui.getSession() : null;
- }
-
- private boolean isCompatibleWithProperty(Renderer<?> renderer,
- LegacyConverter<?, ?> converter) {
- Class<?> type;
- if (converter == null) {
- type = getModelType();
- } else {
- type = converter.getPresentationType();
- }
- return renderer.getPresentationType().isAssignableFrom(type);
- }
-
- private Class<?> getModelType() {
- return grid.getContainerDataSource()
- .getType(grid.getPropertyIdByColumnId(state.id));
- }
-
- /**
- * Sets whether this column is sortable by the user. The grid can be
- * sorted by a sortable column by clicking or tapping the column's
- * default header. Programmatic sorting using the Grid#sort methods is
- * not affected by this setting.
- *
- * @param sortable
- * {@code true} if the user should be able to sort the
- * column, {@code false} otherwise
- * @return the column itself
- *
- * @throws IllegalStateException
- * if the data source of the Grid does not implement
- * {@link Sortable}
- * @throws IllegalStateException
- * if the data source does not support sorting by the
- * property associated with this column
- */
- public Column setSortable(boolean sortable) {
- checkColumnIsAttached();
-
- if (sortable) {
- if (!(grid.datasource instanceof Sortable)) {
- throw new IllegalStateException("Can't set column "
- + toString()
- + " sortable. The Container of Grid does not implement Sortable");
- } else if (!((Sortable) grid.datasource)
- .getSortableContainerPropertyIds()
- .contains(propertyId)) {
- throw new IllegalStateException(
- "Can't set column " + toString()
- + " sortable. Container doesn't support sorting by property "
- + propertyId);
- }
- }
-
- state.sortable = sortable;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Returns whether the user can sort the grid by this column.
- * <p>
- * <em>Note:</em> it is possible to sort by this column programmatically
- * using the Grid#sort methods regardless of the returned value.
- *
- * @return {@code true} if the column is sortable by the user,
- * {@code false} otherwise
- */
- public boolean isSortable() {
- return state.sortable;
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[propertyId:"
- + grid.getPropertyIdByColumnId(state.id) + "]";
- }
-
- /**
- * Sets the ratio with which the column expands.
- * <p>
- * By default, all columns expand equally (treated as if all of them had
- * an expand ratio of 1). Once at least one column gets a defined expand
- * ratio, the implicit expand ratio is removed, and only the defined
- * expand ratios are taken into account.
- * <p>
- * If a column has a defined width ({@link #setWidth(double)}), it
- * overrides this method's effects.
- * <p>
- * <em>Example:</em> A grid with three columns, with expand ratios 0, 1
- * and 2, respectively. The column with a <strong>ratio of 0 is exactly
- * as wide as its contents requires</strong>. The column with a ratio of
- * 1 is as wide as it needs, <strong>plus a third of any excess
- * space</strong>, because we have 3 parts total, and this column
- * reserves only one of those. The column with a ratio of 2, is as wide
- * as it needs to be, <strong>plus two thirds</strong> of the excess
- * width.
- *
- * @param expandRatio
- * the expand ratio of this column. {@code 0} to not have it
- * expand at all. A negative number to clear the expand
- * value.
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- * @see #setWidth(double)
- */
- public Column setExpandRatio(int expandRatio)
- throws IllegalStateException {
- checkColumnIsAttached();
-
- getState().expandRatio = expandRatio;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Returns the column's expand ratio.
- *
- * @return the column's expand ratio
- * @see #setExpandRatio(int)
- */
- public int getExpandRatio() {
- return getState().expandRatio;
- }
-
- /**
- * Clears the expand ratio for this column.
- * <p>
- * Equal to calling {@link #setExpandRatio(int) setExpandRatio(-1)}
- *
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public Column clearExpandRatio() throws IllegalStateException {
- return setExpandRatio(-1);
- }
-
- /**
- * Sets the minimum width for this column.
- * <p>
- * This defines the minimum guaranteed pixel width of the column
- * <em>when it is set to expand</em>.
- *
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- * @see #setExpandRatio(int)
- */
- public Column setMinimumWidth(double pixels)
- throws IllegalStateException {
- checkColumnIsAttached();
-
- final double maxwidth = getMaximumWidth();
- if (pixels >= 0 && pixels > maxwidth && maxwidth >= 0) {
- throw new IllegalArgumentException("New minimum width ("
- + pixels + ") was greater than maximum width ("
- + maxwidth + ")");
- }
- getState().minWidth = pixels;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Return the minimum width for this column.
- *
- * @return the minimum width for this column
- * @see #setMinimumWidth(double)
- */
- public double getMinimumWidth() {
- return getState().minWidth;
- }
-
- /**
- * Sets the maximum width for this column.
- * <p>
- * This defines the maximum allowed pixel width of the column <em>when
- * it is set to expand</em>.
- *
- * @param pixels
- * the maximum width
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- * @see #setExpandRatio(int)
- */
- public Column setMaximumWidth(double pixels) {
- checkColumnIsAttached();
-
- final double minwidth = getMinimumWidth();
- if (pixels >= 0 && pixels < minwidth && minwidth >= 0) {
- throw new IllegalArgumentException("New maximum width ("
- + pixels + ") was less than minimum width (" + minwidth
- + ")");
- }
-
- getState().maxWidth = pixels;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Returns the maximum width for this column.
- *
- * @return the maximum width for this column
- * @see #setMaximumWidth(double)
- */
- public double getMaximumWidth() {
- return getState().maxWidth;
- }
-
- /**
- * Sets whether the properties corresponding to this column should be
- * editable when the item editor is active. By default columns are
- * editable.
- * <p>
- * Values in non-editable columns are currently not displayed when the
- * editor is active, but this will probably change in the future. They
- * are not automatically assigned an editor field and, if one is
- * manually assigned, it is not used. Columns that cannot (or should
- * not) be edited even in principle should be set non-editable.
- *
- * @param editable
- * {@code true} if this column should be editable,
- * {@code false} otherwise
- * @return this column
- *
- * @throws IllegalStateException
- * if the editor is currently active
- *
- * @see LegacyGrid#editItem(Object)
- * @see LegacyGrid#isEditorActive()
- */
- public Column setEditable(boolean editable) {
- checkColumnIsAttached();
- if (grid.isEditorActive()) {
- throw new IllegalStateException(
- "Cannot change column editable status while the editor is active");
- }
- getState().editable = editable;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Returns whether the properties corresponding to this column should be
- * editable when the item editor is active.
- *
- * @return {@code true} if this column is editable, {@code false}
- * otherwise
- *
- * @see LegacyGrid#editItem(Object)
- * @see #setEditable(boolean)
- */
-
- public boolean isEditable() {
- return getState().editable;
- }
-
- /**
- * Sets the field component used to edit the properties in this column
- * when the item editor is active. If an item has not been set, then the
- * binding is postponed until the item is set using
- * {@link #editItem(Object)}.
- * <p>
- * Setting the field to <code>null</code> clears any previously set
- * field, causing a new field to be created the next time the item
- * editor is opened.
- *
- * @param editor
- * the editor field
- * @return this column
- */
- public Column setEditorField(LegacyField<?> editor) {
- grid.setEditorField(getPropertyId(), editor);
- return this;
- }
-
- /**
- * Returns the editor field used to edit the properties in this column
- * when the item editor is active. Returns null if the column is not
- * {@link Column#isEditable() editable}.
- * <p>
- * When {@link #editItem(Object) editItem} is called, fields are
- * automatically created and bound for any unbound properties.
- * <p>
- * Getting a field before the editor has been opened depends on special
- * support from the {@link FieldGroup} in use. Using this method with a
- * user-provided <code>FieldGroup</code> might cause
- * {@link com.vaadin.data.fieldgroup.FieldGroup.BindException
- * BindException} to be thrown.
- *
- * @return the bound field; or <code>null</code> if the respective
- * column is not editable
- *
- * @throws IllegalArgumentException
- * if there is no column for the provided property id
- * @throws FieldGroup.BindException
- * if no field has been configured and there is a problem
- * building or binding
- */
- public LegacyField<?> getEditorField() {
- return grid.getEditorField(getPropertyId());
- }
-
- /**
- * Hides or shows the column. By default columns are visible before
- * explicitly hiding them.
- *
- * @since 7.5.0
- * @param hidden
- * <code>true</code> to hide the column, <code>false</code>
- * to show
- * @return this column
- */
- public Column setHidden(boolean hidden) {
- if (hidden != getState().hidden) {
- getState().hidden = hidden;
- grid.markAsDirty();
- grid.fireColumnVisibilityChangeEvent(this, hidden, false);
- }
- return this;
- }
-
- /**
- * Returns whether this column is hidden. Default is {@code false}.
- *
- * @since 7.5.0
- * @return <code>true</code> if the column is currently hidden,
- * <code>false</code> otherwise
- */
- public boolean isHidden() {
- return getState().hidden;
- }
-
- /**
- * Sets whether this column can be hidden by the user. Hidable columns
- * can be hidden and shown via the sidebar menu.
- *
- * @since 7.5.0
- * @param hidable
- * <code>true</code> iff the column may be hidable by the
- * user via UI interaction
- * @return this column
- */
- public Column setHidable(boolean hidable) {
- if (hidable != getState().hidable) {
- getState().hidable = hidable;
- grid.markAsDirty();
- }
- return this;
- }
-
- /**
- * Returns whether this column can be hidden by the user. Default is
- * {@code false}.
- * <p>
- * <em>Note:</em> the column can be programmatically hidden using
- * {@link #setHidden(boolean)} regardless of the returned value.
- *
- * @since 7.5.0
- * @return <code>true</code> if the user can hide the column,
- * <code>false</code> if not
- */
- public boolean isHidable() {
- return getState().hidable;
- }
-
- /**
- * Sets whether this column can be resized by the user.
- *
- * @since 7.6
- * @param resizable
- * {@code true} if this column should be resizable,
- * {@code false} otherwise
- */
- public Column setResizable(boolean resizable) {
- if (resizable != getState().resizable) {
- getState().resizable = resizable;
- grid.markAsDirty();
- }
- return this;
- }
-
- /**
- * Returns whether this column can be resized by the user. Default is
- * {@code true}.
- * <p>
- * <em>Note:</em> the column can be programmatically resized using
- * {@link #setWidth(double)} and {@link #setWidthUndefined()} regardless
- * of the returned value.
- *
- * @since 7.6
- * @return {@code true} if this column is resizable, {@code false}
- * otherwise
- */
- public boolean isResizable() {
- return getState().resizable;
- }
-
- /**
- * Writes the design attributes for this column into given element.
- *
- * @since 7.5.0
- *
- * @param design
- * Element to write attributes into
- *
- * @param designContext
- * the design context
- */
- protected void writeDesign(Element design,
- DesignContext designContext) {
- Attributes attributes = design.attributes();
- GridColumnState def = new GridColumnState();
-
- DesignAttributeHandler.writeAttribute("property-id", attributes,
- getPropertyId(), null, Object.class);
-
- // Sortable is a special attribute that depends on the container.
- DesignAttributeHandler.writeAttribute("sortable", attributes,
- isSortable(), null, boolean.class);
- DesignAttributeHandler.writeAttribute("editable", attributes,
- isEditable(), def.editable, boolean.class);
- DesignAttributeHandler.writeAttribute("resizable", attributes,
- isResizable(), def.resizable, boolean.class);
-
- DesignAttributeHandler.writeAttribute("hidable", attributes,
- isHidable(), def.hidable, boolean.class);
- DesignAttributeHandler.writeAttribute("hidden", attributes,
- isHidden(), def.hidden, boolean.class);
- DesignAttributeHandler.writeAttribute("hiding-toggle-caption",
- attributes, getHidingToggleCaption(), null, String.class);
-
- DesignAttributeHandler.writeAttribute("width", attributes,
- getWidth(), def.width, Double.class);
- DesignAttributeHandler.writeAttribute("min-width", attributes,
- getMinimumWidth(), def.minWidth, Double.class);
- DesignAttributeHandler.writeAttribute("max-width", attributes,
- getMaximumWidth(), def.maxWidth, Double.class);
- DesignAttributeHandler.writeAttribute("expand", attributes,
- getExpandRatio(), def.expandRatio, Integer.class);
- }
-
- /**
- * Reads the design attributes for this column from given element.
- *
- * @since 7.5.0
- * @param design
- * Element to read attributes from
- * @param designContext
- * the design context
- */
- protected void readDesign(Element design, DesignContext designContext) {
- Attributes attributes = design.attributes();
-
- if (design.hasAttr("sortable")) {
- setSortable(DesignAttributeHandler.readAttribute("sortable",
- attributes, boolean.class));
- }
- if (design.hasAttr("editable")) {
- setEditable(DesignAttributeHandler.readAttribute("editable",
- attributes, boolean.class));
- }
- if (design.hasAttr("resizable")) {
- setResizable(DesignAttributeHandler.readAttribute("resizable",
- attributes, boolean.class));
- }
-
- if (design.hasAttr("hidable")) {
- setHidable(DesignAttributeHandler.readAttribute("hidable",
- attributes, boolean.class));
- }
- if (design.hasAttr("hidden")) {
- setHidden(DesignAttributeHandler.readAttribute("hidden",
- attributes, boolean.class));
- }
- if (design.hasAttr("hiding-toggle-caption")) {
- setHidingToggleCaption(DesignAttributeHandler.readAttribute(
- "hiding-toggle-caption", attributes, String.class));
- }
-
- // Read size info where necessary.
- if (design.hasAttr("width")) {
- setWidth(DesignAttributeHandler.readAttribute("width",
- attributes, Double.class));
- }
- if (design.hasAttr("min-width")) {
- setMinimumWidth(DesignAttributeHandler
- .readAttribute("min-width", attributes, Double.class));
- }
- if (design.hasAttr("max-width")) {
- setMaximumWidth(DesignAttributeHandler
- .readAttribute("max-width", attributes, Double.class));
- }
- if (design.hasAttr("expand")) {
- if (design.attr("expand").isEmpty()) {
- setExpandRatio(1);
- } else {
- setExpandRatio(DesignAttributeHandler.readAttribute(
- "expand", attributes, Integer.class));
- }
- }
- }
- }
-
- /**
- * An abstract base class for server-side
- * {@link com.vaadin.ui.renderers.Renderer Grid renderers}. This class
- * currently extends the AbstractExtension superclass, but this fact should
- * be regarded as an implementation detail and subject to change in a future
- * major or minor Vaadin revision.
- *
- * @param <T>
- * the type this renderer knows how to present
- */
- public static abstract class AbstractRenderer<T>
- extends AbstractGridExtension implements Renderer<T> {
-
- private final Class<T> presentationType;
-
- private final String nullRepresentation;
-
- protected AbstractRenderer(Class<T> presentationType,
- String nullRepresentation) {
- this.presentationType = presentationType;
- this.nullRepresentation = nullRepresentation;
- }
-
- protected AbstractRenderer(Class<T> presentationType) {
- this(presentationType, null);
- }
-
- /**
- * This method is inherited from AbstractExtension but should never be
- * called directly with an AbstractRenderer.
- */
- @Deprecated
- @Override
- protected Class<LegacyGrid> getSupportedParentType() {
- return LegacyGrid.class;
- }
-
- /**
- * This method is inherited from AbstractExtension but should never be
- * called directly with an AbstractRenderer.
- */
- @Deprecated
- @Override
- protected void extend(AbstractClientConnector target) {
- super.extend(target);
- }
-
- @Override
- public Class<T> getPresentationType() {
- return presentationType;
- }
-
- @Override
- public JsonValue encode(T value) {
- if (value == null) {
- return encode(getNullRepresentation(), String.class);
- } else {
- return encode(value, getPresentationType());
- }
- }
-
- /**
- * Null representation for the renderer
- *
- * @return a textual representation of {@code null}
- */
- protected String getNullRepresentation() {
- return nullRepresentation;
- }
-
- /**
- * Encodes the given value to JSON.
- * <p>
- * This is a helper method that can be invoked by an
- * {@link #encode(Object) encode(T)} override if serializing a value of
- * type other than {@link #getPresentationType() the presentation type}
- * is desired. For instance, a {@code Renderer<Date>} could first turn a
- * date value into a formatted string and return
- * {@code encode(dateString, String.class)}.
- *
- * @param value
- * the value to be encoded
- * @param type
- * the type of the value
- * @return a JSON representation of the given value
- */
- protected <U> JsonValue encode(U value, Class<U> type) {
- return JsonCodec
- .encode(value, null, type, getUI().getConnectorTracker())
- .getEncodedValue();
- }
-
- /**
- * Converts and encodes the given data model property value using the
- * given converter and renderer. This method is public only for testing
- * purposes.
- *
- * @since 7.6
- * @param renderer
- * the renderer to use
- * @param converter
- * the converter to use
- * @param modelValue
- * the value to convert and encode
- * @param locale
- * the locale to use in conversion
- * @return an encoded value ready to be sent to the client
- */
- public static <T> JsonValue encodeValue(Object modelValue,
- Renderer<T> renderer, LegacyConverter<?, ?> converter,
- Locale locale) {
- Class<T> presentationType = renderer.getPresentationType();
- T presentationValue;
-
- if (converter == null) {
- try {
- presentationValue = presentationType.cast(modelValue);
- } catch (ClassCastException e) {
- if (presentationType == String.class) {
- // If there is no converter, just fallback to using
- // toString(). modelValue can't be null as
- // Class.cast(null) will always succeed
- presentationValue = (T) modelValue.toString();
- } else {
- throw new LegacyConverter.ConversionException(
- "Unable to convert value of type "
- + modelValue.getClass().getName()
- + " to presentation type "
- + presentationType.getName()
- + ". No converter is set and the types are not compatible.");
- }
- }
- } else {
- assert presentationType
- .isAssignableFrom(converter.getPresentationType());
- @SuppressWarnings("unchecked")
- LegacyConverter<T, Object> safeConverter = (LegacyConverter<T, Object>) converter;
- presentationValue = safeConverter.convertToPresentation(
- modelValue, safeConverter.getPresentationType(),
- locale);
- }
-
- JsonValue encodedValue;
- try {
- encodedValue = renderer.encode(presentationValue);
- } catch (Exception e) {
- getLogger().log(Level.SEVERE, "Unable to encode data", e);
- encodedValue = renderer.encode(null);
- }
-
- return encodedValue;
- }
-
- private static Logger getLogger() {
- return Logger.getLogger(AbstractRenderer.class.getName());
- }
-
- }
-
- /**
- * An abstract base class for server-side Grid extensions.
- * <p>
- * Note: If the extension is an instance of {@link DataGenerator} it will
- * automatically register itself to {@link RpcDataProviderExtension} of
- * extended Grid. On remove this registration is automatically removed.
- *
- * @since 7.5
- */
- public static abstract class AbstractGridExtension
- extends AbstractExtension {
-
- /**
- * Constructs a new Grid extension.
- */
- public AbstractGridExtension() {
- super();
- }
-
- /**
- * Constructs a new Grid extension and extends given Grid.
- *
- * @param grid
- * a grid instance
- */
- public AbstractGridExtension(LegacyGrid grid) {
- super();
- extend(grid);
- }
-
- @Override
- protected void extend(AbstractClientConnector target) {
- super.extend(target);
-
- if (this instanceof DataGenerator) {
- getParentGrid().datasourceExtension
- .addDataGenerator((DataGenerator) this);
- }
- }
-
- @Override
- public void remove() {
- if (this instanceof DataGenerator) {
- getParentGrid().datasourceExtension
- .removeDataGenerator((DataGenerator) this);
- }
-
- super.remove();
- }
-
- /**
- * Gets the item id for a row key.
- * <p>
- * A key is used to identify a particular row on both a server and a
- * client. This method can be used to get the item id for the row key
- * that the client has sent.
- *
- * @param rowKey
- * the row key for which to retrieve an item id
- * @return the item id corresponding to {@code key}
- */
- protected Object getItemId(String rowKey) {
- return getParentGrid().getKeyMapper().get(rowKey);
- }
-
- /**
- * Gets the column for a column id.
- * <p>
- * An id is used to identify a particular column on both a server and a
- * client. This method can be used to get the column for the column id
- * that the client has sent.
- *
- * @param columnId
- * the column id for which to retrieve a column
- * @return the column corresponding to {@code columnId}
- */
- protected Column getColumn(String columnId) {
- return getParentGrid().getColumnByColumnId(columnId);
- }
-
- /**
- * Gets the parent Grid of the renderer.
- *
- * @return parent grid
- * @throws IllegalStateException
- * if parent is not Grid
- */
- protected LegacyGrid getParentGrid() {
- if (getParent() instanceof LegacyGrid) {
- LegacyGrid grid = (LegacyGrid) getParent();
- return grid;
- } else if (getParent() == null) {
- throw new IllegalStateException(
- "Renderer is not attached to any parent");
- } else {
- throw new IllegalStateException(
- "Renderers can be used only with Grid. Extended "
- + getParent().getClass().getSimpleName()
- + " instead");
- }
- }
-
- /**
- * Resends the row data for given item id to the client.
- *
- * @since 7.6
- * @param itemId
- * row to refresh
- */
- protected void refreshRow(Object itemId) {
- getParentGrid().datasourceExtension.updateRowData(itemId);
- }
-
- /**
- * Informs the parent Grid that this Extension wants to add a child
- * component to it.
- *
- * @since 7.6
- * @param c
- * component
- */
- protected void addComponentToGrid(Component c) {
- getParentGrid().addComponent(c);
- }
-
- /**
- * Informs the parent Grid that this Extension wants to remove a child
- * component from it.
- *
- * @since 7.6
- * @param c
- * component
- */
- protected void removeComponentFromGrid(Component c) {
- getParentGrid().removeComponent(c);
- }
- }
-
- /**
- * The data source attached to the grid
- */
- private Container.Indexed datasource;
-
- /**
- * Property id to column instance mapping
- */
- private final Map<Object, Column> columns = new HashMap<>();
-
- /**
- * Key generator for column server-to-client communication
- */
- private final KeyMapper<Object> columnKeys = new KeyMapper<>();
-
- /**
- * The current sort order
- */
- private final List<SortOrder> sortOrder = new ArrayList<>();
-
- /**
- * Property listener for listening to changes in data source properties.
- */
- private final PropertySetChangeListener propertyListener = new PropertySetChangeListener() {
-
- @Override
- public void containerPropertySetChange(PropertySetChangeEvent event) {
- Collection<?> properties = new HashSet<Object>(
- event.getContainer().getContainerPropertyIds());
-
- // Find columns that need to be removed.
- List<Column> removedColumns = new LinkedList<>();
- for (Object propertyId : columns.keySet()) {
- if (!properties.contains(propertyId)) {
- removedColumns.add(getColumn(propertyId));
- }
- }
-
- // Actually remove columns.
- for (Column column : removedColumns) {
- Object propertyId = column.getPropertyId();
- internalRemoveColumn(propertyId);
- columnKeys.remove(propertyId);
- }
- datasourceExtension.columnsRemoved(removedColumns);
-
- // Add new columns
- List<Column> addedColumns = new LinkedList<>();
- for (Object propertyId : properties) {
- if (!columns.containsKey(propertyId)) {
- addedColumns.add(appendColumn(propertyId));
- }
- }
- datasourceExtension.columnsAdded(addedColumns);
-
- if (getFrozenColumnCount() > columns.size()) {
- setFrozenColumnCount(columns.size());
- }
-
- // Unset sortable for non-sortable columns.
- if (datasource instanceof Sortable) {
- Collection<?> sortables = ((Sortable) datasource)
- .getSortableContainerPropertyIds();
- for (Object propertyId : columns.keySet()) {
- Column column = columns.get(propertyId);
- if (!sortables.contains(propertyId)
- && column.isSortable()) {
- column.setSortable(false);
- }
- }
- }
- }
- };
-
- private final ItemSetChangeListener editorClosingItemSetListener = new ItemSetChangeListener() {
- @Override
- public void containerItemSetChange(ItemSetChangeEvent event) {
- cancelEditor();
- }
- };
-
- private RpcDataProviderExtension datasourceExtension;
-
- /**
- * The selection model that is currently in use. Never <code>null</code>
- * after the constructor has been run.
- */
- private SelectionModel selectionModel;
-
- /**
- * Used to know whether selection change events originate from the server or
- * the client so the selection change handler knows whether the changes
- * should be sent to the client.
- */
- private boolean applyingSelectionFromClient;
-
- private final Header header = new Header(this);
- private final Footer footer = new Footer(this);
-
- private Object editedItemId = null;
- private boolean editorActive = false;
- private FieldGroup editorFieldGroup = new CustomFieldGroup();
-
- private CellStyleGenerator cellStyleGenerator;
- private RowStyleGenerator rowStyleGenerator;
-
- private CellDescriptionGenerator cellDescriptionGenerator;
- private RowDescriptionGenerator rowDescriptionGenerator;
-
- /**
- * <code>true</code> if Grid is using the internal IndexedContainer created
- * in Grid() constructor, or <code>false</code> if the user has set their
- * own Container.
- *
- * @see #setContainerDataSource(Indexed)
- * @see #LegacyGrid()
- */
- private boolean defaultContainer = true;
-
- private EditorErrorHandler editorErrorHandler = new DefaultEditorErrorHandler();
-
- private DetailComponentManager detailComponentManager = null;
-
- private Set<Component> extensionComponents = new HashSet<>();
-
- private static final Method SELECTION_CHANGE_METHOD = ReflectTools
- .findMethod(SelectionListener.class, "select",
- SelectionEvent.class);
-
- private static final Method SORT_ORDER_CHANGE_METHOD = ReflectTools
- .findMethod(SortListener.class, "sort", SortEvent.class);
-
- private static final Method COLUMN_REORDER_METHOD = ReflectTools.findMethod(
- ColumnReorderListener.class, "columnReorder",
- ColumnReorderEvent.class);
-
- private static final Method COLUMN_RESIZE_METHOD = ReflectTools.findMethod(
- ColumnResizeListener.class, "columnResize",
- ColumnResizeEvent.class);
-
- private static final Method COLUMN_VISIBILITY_METHOD = ReflectTools
- .findMethod(ColumnVisibilityChangeListener.class,
- "columnVisibilityChanged",
- ColumnVisibilityChangeEvent.class);
-
- /**
- * Creates a new Grid with a new {@link IndexedContainer} as the data
- * source.
- */
- public LegacyGrid() {
- this(null, null);
- }
-
- /**
- * Creates a new Grid using the given data source.
- *
- * @param dataSource
- * the indexed container to use as a data source
- */
- public LegacyGrid(final Container.Indexed dataSource) {
- this(null, dataSource);
- }
-
- /**
- * Creates a new Grid with the given caption and a new
- * {@link IndexedContainer} data source.
- *
- * @param caption
- * the caption of the grid
- */
- public LegacyGrid(String caption) {
- this(caption, null);
- }
-
- /**
- * Creates a new Grid with the given caption and data source. If the data
- * source is null, a new {@link IndexedContainer} will be used.
- *
- * @param caption
- * the caption of the grid
- * @param dataSource
- * the indexed container to use as a data source
- */
- public LegacyGrid(String caption, Container.Indexed dataSource) {
- if (dataSource == null) {
- internalSetContainerDataSource(new IndexedContainer());
- } else {
- setContainerDataSource(dataSource);
- }
- setCaption(caption);
- initGrid();
- }
-
- /**
- * Grid initial setup
- */
- private void initGrid() {
- setSelectionMode(getDefaultSelectionMode());
-
- registerRpc(new GridServerRpc() {
-
- @Override
- public void sort(String[] columnIds, SortDirection[] directions,
- boolean userOriginated) {
- assert columnIds.length == directions.length;
-
- List<SortOrder> order = new ArrayList<>(columnIds.length);
- for (int i = 0; i < columnIds.length; i++) {
- Object propertyId = getPropertyIdByColumnId(columnIds[i]);
- order.add(new SortOrder(propertyId, directions[i]));
- }
- setSortOrder(order, userOriginated);
- if (!order.equals(getSortOrder())) {
- /*
- * Actual sort order is not what the client expects. Make
- * sure the client gets a state change event by clearing the
- * diffstate and marking as dirty
- */
- ConnectorTracker connectorTracker = getUI()
- .getConnectorTracker();
- JsonObject diffState = connectorTracker
- .getDiffState(LegacyGrid.this);
- diffState.remove("sortColumns");
- diffState.remove("sortDirs");
- markAsDirty();
- }
- }
-
- @Override
- public void itemClick(String rowKey, String columnId,
- MouseEventDetails details) {
- Object itemId = getKeyMapper().get(rowKey);
- Item item = datasource.getItem(itemId);
- Object propertyId = getPropertyIdByColumnId(columnId);
- fireEvent(new ItemClickEvent(LegacyGrid.this, item, itemId,
- propertyId, details));
- }
-
- @Override
- public void columnsReordered(List<String> newColumnOrder,
- List<String> oldColumnOrder) {
- final String diffStateKey = "columnOrder";
- ConnectorTracker connectorTracker = getUI()
- .getConnectorTracker();
- JsonObject diffState = connectorTracker.getDiffState(LegacyGrid.this);
- // discard the change if the columns have been reordered from
- // the server side, as the server side is always right
- if (getState(false).columnOrder.equals(oldColumnOrder)) {
- // Don't mark as dirty since client has the state already
- getState(false).columnOrder = newColumnOrder;
- // write changes to diffState so that possible reverting the
- // column order is sent to client
- assert diffState
- .hasKey(diffStateKey) : "Field name has changed";
- Type type = null;
- try {
- type = (getState(false).getClass()
- .getDeclaredField(diffStateKey)
- .getGenericType());
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- }
- EncodeResult encodeResult = JsonCodec.encode(
- getState(false).columnOrder, diffState, type,
- connectorTracker);
-
- diffState.put(diffStateKey, encodeResult.getEncodedValue());
- fireColumnReorderEvent(true);
- } else {
- // make sure the client is reverted to the order that the
- // server thinks it is
- diffState.remove(diffStateKey);
- markAsDirty();
- }
- }
-
- @Override
- public void columnVisibilityChanged(String id, boolean hidden,
- boolean userOriginated) {
- final Column column = getColumnByColumnId(id);
- final GridColumnState columnState = column.getState();
-
- if (columnState.hidden != hidden) {
- columnState.hidden = hidden;
-
- final String diffStateKey = "columns";
- ConnectorTracker connectorTracker = getUI()
- .getConnectorTracker();
- JsonObject diffState = connectorTracker
- .getDiffState(LegacyGrid.this);
-
- assert diffState
- .hasKey(diffStateKey) : "Field name has changed";
- Type type = null;
- try {
- type = (getState(false).getClass()
- .getDeclaredField(diffStateKey)
- .getGenericType());
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- }
- EncodeResult encodeResult = JsonCodec.encode(
- getState(false).columns, diffState, type,
- connectorTracker);
-
- diffState.put(diffStateKey, encodeResult.getEncodedValue());
-
- fireColumnVisibilityChangeEvent(column, hidden,
- userOriginated);
- }
- }
-
- @Override
- public void contextClick(int rowIndex, String rowKey,
- String columnId, Section section,
- MouseEventDetails details) {
- Object itemId = null;
- if (rowKey != null) {
- itemId = getKeyMapper().get(rowKey);
- }
- fireEvent(new GridContextClickEvent(LegacyGrid.this, details, section,
- rowIndex, itemId, getPropertyIdByColumnId(columnId)));
- }
-
- @Override
- public void columnResized(String id, double pixels) {
- final Column column = getColumnByColumnId(id);
- if (column != null && column.isResizable()) {
- column.getState().width = pixels;
- fireColumnResizeEvent(column, true);
- markAsDirty();
- }
- }
- });
-
- registerRpc(new EditorServerRpc() {
-
- @Override
- public void bind(int rowIndex) {
- try {
- Object id = getContainerDataSource().getIdByIndex(rowIndex);
-
- final boolean opening = editedItemId == null;
-
- final boolean moving = !opening && !editedItemId.equals(id);
-
- final boolean allowMove = !isEditorBuffered()
- && getEditorFieldGroup().isValid();
-
- if (opening || !moving || allowMove) {
- doBind(id);
- } else {
- failBind(null);
- }
- } catch (Exception e) {
- failBind(e);
- }
- }
-
- private void doBind(Object id) {
- editedItemId = id;
- doEditItem();
- getEditorRpc().confirmBind(true);
- }
-
- private void failBind(Exception e) {
- if (e != null) {
- handleError(e);
- }
- getEditorRpc().confirmBind(false);
- }
-
- @Override
- public void cancel(int rowIndex) {
- try {
- // For future proofing even though cannot currently fail
- doCancelEditor();
- } catch (Exception e) {
- handleError(e);
- }
- }
-
- @Override
- public void save(int rowIndex) {
- List<String> errorColumnIds = null;
- String errorMessage = null;
- boolean success = false;
- try {
- saveEditor();
- success = true;
- } catch (CommitException e) {
- try {
- CommitErrorEvent event = new CommitErrorEvent(LegacyGrid.this,
- e);
- getEditorErrorHandler().commitError(event);
-
- errorMessage = event.getUserErrorMessage();
-
- errorColumnIds = new ArrayList<>();
- for (Column column : event.getErrorColumns()) {
- errorColumnIds.add(column.state.id);
- }
- } catch (Exception ee) {
- // A badly written error handler can throw an exception,
- // which would lock up the Grid
- handleError(ee);
- }
- } catch (Exception e) {
- handleError(e);
- }
- getEditorRpc().confirmSave(success, errorMessage,
- errorColumnIds);
- }
-
- private void handleError(Exception e) {
- com.vaadin.server.ErrorEvent.findErrorHandler(LegacyGrid.this)
- .error(new ConnectorErrorEvent(LegacyGrid.this, e));
- }
- });
- }
-
- @Override
- public void beforeClientResponse(boolean initial) {
- try {
- header.sanityCheck();
- footer.sanityCheck();
- } catch (Exception e) {
- e.printStackTrace();
- setComponentError(new ErrorMessage() {
-
- @Override
- public ErrorLevel getErrorLevel() {
- return ErrorLevel.CRITICAL;
- }
-
- @Override
- public String getFormattedHtmlMessage() {
- return "Incorrectly merged cells";
- }
-
- });
- }
-
- super.beforeClientResponse(initial);
- }
-
- /**
- * Sets the grid data source.
- * <p>
- *
- * <strong>Note</strong> Grid columns are based on properties and try to
- * detect a correct converter for the data type. The columns are not
- * reinitialized automatically if the container is changed, and if the same
- * properties are present after container change, the columns are reused.
- * Properties with same names, but different data types will lead to
- * unpredictable behaviour.
- *
- * @param container
- * The container data source. Cannot be null.
- * @throws IllegalArgumentException
- * if the data source is null
- */
- public void setContainerDataSource(Container.Indexed container) {
- defaultContainer = false;
- internalSetContainerDataSource(container);
- }
-
- private void internalSetContainerDataSource(Container.Indexed container) {
- if (container == null) {
- throw new IllegalArgumentException(
- "Cannot set the datasource to null");
- }
- if (datasource == container) {
- return;
- }
-
- // Remove old listeners
- if (datasource instanceof PropertySetChangeNotifier) {
- ((PropertySetChangeNotifier) datasource)
- .removePropertySetChangeListener(propertyListener);
- }
-
- if (datasourceExtension != null) {
- removeExtension(datasourceExtension);
- }
-
- // Remove old DetailComponentManager
- if (detailComponentManager != null) {
- detailComponentManager.remove();
- }
-
- resetEditor();
-
- datasource = container;
-
- //
- // Adjust sort order
- //
-
- if (container instanceof Container.Sortable) {
-
- // If the container is sortable, go through the current sort order
- // and match each item to the sortable properties of the new
- // container. If the new container does not support an item in the
- // current sort order, that item is removed from the current sort
- // order list.
- Collection<?> sortableProps = ((Container.Sortable) getContainerDataSource())
- .getSortableContainerPropertyIds();
-
- Iterator<SortOrder> i = sortOrder.iterator();
- while (i.hasNext()) {
- if (!sortableProps.contains(i.next().getPropertyId())) {
- i.remove();
- }
- }
-
- sort(false);
- } else {
- // Clear sorting order. Don't sort.
- sortOrder.clear();
- }
-
- datasourceExtension = new RpcDataProviderExtension(container);
- datasourceExtension.extend(this);
- datasourceExtension.addDataGenerator(new RowDataGenerator());
- for (Extension e : getExtensions()) {
- if (e instanceof DataGenerator) {
- datasourceExtension.addDataGenerator((DataGenerator) e);
- }
- }
-
- if (detailComponentManager != null) {
- detailComponentManager = new DetailComponentManager(this,
- detailComponentManager.getDetailsGenerator());
- } else {
- detailComponentManager = new DetailComponentManager(this);
- }
-
- /*
- * selectionModel == null when the invocation comes from the
- * constructor.
- */
- if (selectionModel != null) {
- selectionModel.reset();
- }
-
- // Listen to changes in properties and remove columns if needed
- if (datasource instanceof PropertySetChangeNotifier) {
- ((PropertySetChangeNotifier) datasource)
- .addPropertySetChangeListener(propertyListener);
- }
-
- /*
- * activeRowHandler will be updated by the client-side request that
- * occurs on container change - no need to actively re-insert any
- * ValueChangeListeners at this point.
- */
-
- setFrozenColumnCount(0);
-
- if (columns.isEmpty()) {
- // Add columns
- for (Object propertyId : datasource.getContainerPropertyIds()) {
- Column column = appendColumn(propertyId);
-
- // Initial sorting is defined by container
- if (datasource instanceof Sortable) {
- column.setSortable(((Sortable) datasource)
- .getSortableContainerPropertyIds()
- .contains(propertyId));
- } else {
- column.setSortable(false);
- }
- }
- } else {
- Collection<?> properties = datasource.getContainerPropertyIds();
- for (Object property : columns.keySet()) {
- if (!properties.contains(property)) {
- throw new IllegalStateException(
- "Found at least one column in Grid that does not exist in the given container: "
- + property + " with the header \""
- + getColumn(property).getHeaderCaption()
- + "\". "
- + "Call removeAllColumns() before setContainerDataSource() if you want to reconfigure the columns based on the new container.");
- }
-
- if (!(datasource instanceof Sortable)
- || !((Sortable) datasource)
- .getSortableContainerPropertyIds()
- .contains(property)) {
- columns.get(property).setSortable(false);
- }
- }
- }
- }
-
- /**
- * Returns the grid data source.
- *
- * @return the container data source of the grid
- */
- public Container.Indexed getContainerDataSource() {
- return datasource;
- }
-
- /**
- * Returns a column based on the property id
- *
- * @param propertyId
- * the property id of the column
- * @return the column or <code>null</code> if not found
- */
- public Column getColumn(Object propertyId) {
- return columns.get(propertyId);
- }
-
- /**
- * Returns a copy of currently configures columns in their current visual
- * order in this Grid.
- *
- * @return unmodifiable copy of current columns in visual order
- */
- public List<Column> getColumns() {
- List<Column> columns = new ArrayList<>();
- for (String columnId : getState(false).columnOrder) {
- columns.add(getColumnByColumnId(columnId));
- }
- return Collections.unmodifiableList(columns);
- }
-
- /**
- * Adds a new Column to Grid. Also adds the property to container with data
- * type String, if property for column does not exist in it. Default value
- * for the new property is an empty String.
- * <p>
- * Note that adding a new property is only done for the default container
- * that Grid sets up with the default constructor.
- *
- * @param propertyId
- * the property id of the new column
- * @return the new column
- *
- * @throws IllegalStateException
- * if column for given property already exists in this grid
- */
-
- public Column addColumn(Object propertyId) throws IllegalStateException {
- if (datasource.getContainerPropertyIds().contains(propertyId)
- && !columns.containsKey(propertyId)) {
- appendColumn(propertyId);
- } else if (defaultContainer) {
- addColumnProperty(propertyId, String.class, "");
- } else {
- if (columns.containsKey(propertyId)) {
- throw new IllegalStateException(
- "A column for property id '" + propertyId.toString()
- + "' already exists in this grid");
- } else {
- throw new IllegalStateException(
- "Property id '" + propertyId.toString()
- + "' does not exist in the container");
- }
- }
-
- // Inform the data provider of this new column.
- Column column = getColumn(propertyId);
- List<Column> addedColumns = new ArrayList<>();
- addedColumns.add(column);
- datasourceExtension.columnsAdded(addedColumns);
-
- return column;
- }
-
- /**
- * Adds a new Column to Grid. This function makes sure that the property
- * with the given id and data type exists in the container. If property does
- * not exists, it will be created.
- * <p>
- * Default value for the new property is 0 if type is Integer, Double and
- * Float. If type is String, default value is an empty string. For all other
- * types the default value is null.
- * <p>
- * Note that adding a new property is only done for the default container
- * that Grid sets up with the default constructor.
- *
- * @param propertyId
- * the property id of the new column
- * @param type
- * the data type for the new property
- * @return the new column
- *
- * @throws IllegalStateException
- * if column for given property already exists in this grid or
- * property already exists in the container with wrong type
- */
- public Column addColumn(Object propertyId, Class<?> type) {
- addColumnProperty(propertyId, type, null);
- return getColumn(propertyId);
- }
-
- protected void addColumnProperty(Object propertyId, Class<?> type,
- Object defaultValue) throws IllegalStateException {
- if (!defaultContainer) {
- throw new IllegalStateException(
- "Container for this Grid is not a default container from Grid() constructor");
- }
-
- if (!columns.containsKey(propertyId)) {
- if (!datasource.getContainerPropertyIds().contains(propertyId)) {
- datasource.addContainerProperty(propertyId, type, defaultValue);
- } else {
- Property<?> containerProperty = datasource.getContainerProperty(
- datasource.firstItemId(), propertyId);
- if (containerProperty.getType() == type) {
- appendColumn(propertyId);
- } else {
- throw new IllegalStateException(
- "DataSource already has the given property "
- + propertyId + " with a different type");
- }
- }
- } else {
- throw new IllegalStateException(
- "Grid already has a column for property " + propertyId);
- }
- }
-
- /**
- * Removes all columns from this Grid.
- */
- public void removeAllColumns() {
- List<Column> removed = new ArrayList<>(columns.values());
- Set<Object> properties = new HashSet<>(columns.keySet());
- for (Object propertyId : properties) {
- removeColumn(propertyId);
- }
- datasourceExtension.columnsRemoved(removed);
- }
-
- /**
- * Used internally by the {@link LegacyGrid} to get a {@link Column} by
- * referencing its generated state id. Also used by {@link Column} to verify
- * if it has been detached from the {@link LegacyGrid}.
- *
- * @param columnId
- * the client id generated for the column when the column is
- * added to the grid
- * @return the column with the id or <code>null</code> if not found
- */
- Column getColumnByColumnId(String columnId) {
- Object propertyId = getPropertyIdByColumnId(columnId);
- return getColumn(propertyId);
- }
-
- /**
- * Used internally by the {@link LegacyGrid} to get a property id by referencing
- * the columns generated state id.
- *
- * @param columnId
- * The state id of the column
- * @return The column instance or null if not found
- */
- Object getPropertyIdByColumnId(String columnId) {
- return columnKeys.get(columnId);
- }
-
- /**
- * Returns whether column reordering is allowed. Default value is
- * <code>false</code>.
- *
- * @since 7.5.0
- * @return true if reordering is allowed
- */
- public boolean isColumnReorderingAllowed() {
- return getState(false).columnReorderingAllowed;
- }
-
- /**
- * Sets whether or not column reordering is allowed. Default value is
- * <code>false</code>.
- *
- * @since 7.5.0
- * @param columnReorderingAllowed
- * specifies whether column reordering is allowed
- */
- public void setColumnReorderingAllowed(boolean columnReorderingAllowed) {
- if (isColumnReorderingAllowed() != columnReorderingAllowed) {
- getState().columnReorderingAllowed = columnReorderingAllowed;
- }
- }
-
- @Override
- protected GridState getState() {
- return (GridState) super.getState();
- }
-
- @Override
- protected GridState getState(boolean markAsDirty) {
- return (GridState) super.getState(markAsDirty);
- }
-
- /**
- * Creates a new column based on a property id and appends it as the last
- * column.
- *
- * @param datasourcePropertyId
- * The property id of a property in the datasource
- */
- private Column appendColumn(Object datasourcePropertyId) {
- if (datasourcePropertyId == null) {
- throw new IllegalArgumentException("Property id cannot be null");
- }
- assert datasource.getContainerPropertyIds().contains(
- datasourcePropertyId) : "Datasource should contain the property id";
-
- GridColumnState columnState = new GridColumnState();
- columnState.id = columnKeys.key(datasourcePropertyId);
-
- Column column = new Column(this, columnState, datasourcePropertyId);
- columns.put(datasourcePropertyId, column);
-
- getState().columns.add(columnState);
- getState().columnOrder.add(columnState.id);
- header.addColumn(datasourcePropertyId);
- footer.addColumn(datasourcePropertyId);
-
- String humanFriendlyPropertyId = SharedUtil.propertyIdToHumanFriendly(
- String.valueOf(datasourcePropertyId));
- column.setHeaderCaption(humanFriendlyPropertyId);
-
- if (datasource instanceof Sortable
- && ((Sortable) datasource).getSortableContainerPropertyIds()
- .contains(datasourcePropertyId)) {
- column.setSortable(true);
- }
-
- return column;
- }
-
- /**
- * Removes a column from Grid based on a property id.
- *
- * @param propertyId
- * The property id of column to be removed
- *
- * @throws IllegalArgumentException
- * if there is no column for given property id in this grid
- */
- public void removeColumn(Object propertyId)
- throws IllegalArgumentException {
- if (!columns.keySet().contains(propertyId)) {
- throw new IllegalArgumentException(
- "There is no column for given property id " + propertyId);
- }
-
- List<Column> removed = new ArrayList<>();
- removed.add(getColumn(propertyId));
- internalRemoveColumn(propertyId);
- datasourceExtension.columnsRemoved(removed);
- }
-
- private void internalRemoveColumn(Object propertyId) {
- setEditorField(propertyId, null);
- header.removeColumn(propertyId);
- footer.removeColumn(propertyId);
- Column column = columns.remove(propertyId);
- getState().columnOrder.remove(columnKeys.key(propertyId));
- getState().columns.remove(column.getState());
- removeExtension(column.getRenderer());
- }
-
- /**
- * Sets the columns and their order for the grid. Current columns whose
- * property id is not in propertyIds are removed. Similarly, a column is
- * added for any property id in propertyIds that has no corresponding column
- * in this Grid.
- *
- * @since 7.5.0
- *
- * @param propertyIds
- * properties in the desired column order
- */
- public void setColumns(Object... propertyIds) {
- Set<?> removePids = new HashSet<>(columns.keySet());
- removePids.removeAll(Arrays.asList(propertyIds));
- for (Object removePid : removePids) {
- removeColumn(removePid);
- }
- Set<?> addPids = new HashSet<>(Arrays.asList(propertyIds));
- addPids.removeAll(columns.keySet());
- for (Object propertyId : addPids) {
- addColumn(propertyId);
- }
- setColumnOrder(propertyIds);
- }
-
- /**
- * Sets a new column order for the grid. All columns which are not ordered
- * here will remain in the order they were before as the last columns of
- * grid.
- *
- * @param propertyIds
- * properties in the order columns should be
- */
- public void setColumnOrder(Object... propertyIds) {
- List<String> columnOrder = new ArrayList<>();
- for (Object propertyId : propertyIds) {
- if (columns.containsKey(propertyId)) {
- columnOrder.add(columnKeys.key(propertyId));
- } else {
- throw new IllegalArgumentException(
- "Grid does not contain column for property "
- + String.valueOf(propertyId));
- }
- }
-
- List<String> stateColumnOrder = getState().columnOrder;
- if (stateColumnOrder.size() != columnOrder.size()) {
- stateColumnOrder.removeAll(columnOrder);
- columnOrder.addAll(stateColumnOrder);
- }
- getState().columnOrder = columnOrder;
- fireColumnReorderEvent(false);
- }
-
- /**
- * Sets the number of frozen columns in this grid. Setting the count to 0
- * means that no data columns will be frozen, but the built-in selection
- * checkbox column will still be frozen if it's in use. Setting the count to
- * -1 will also disable the selection column.
- * <p>
- * The default value is 0.
- *
- * @param numberOfColumns
- * the number of columns that should be frozen
- *
- * @throws IllegalArgumentException
- * if the column count is < 0 or > the number of visible columns
- */
- public void setFrozenColumnCount(int numberOfColumns) {
- if (numberOfColumns < -1 || numberOfColumns > columns.size()) {
- throw new IllegalArgumentException(
- "count must be between -1 and the current number of columns ("
- + columns.size() + "): " + numberOfColumns);
- }
-
- getState().frozenColumnCount = numberOfColumns;
- }
-
- /**
- * Gets the number of frozen columns in this grid. 0 means that no data
- * columns will be frozen, but the built-in selection checkbox column will
- * still be frozen if it's in use. -1 means that not even the selection
- * column is frozen.
- * <p>
- * <em>NOTE:</em> this count includes {@link Column#isHidden() hidden
- * columns} in the count.
- *
- * @see #setFrozenColumnCount(int)
- *
- * @return the number of frozen columns
- */
- public int getFrozenColumnCount() {
- return getState(false).frozenColumnCount;
- }
-
- /**
- * Scrolls to a certain item, using {@link ScrollDestination#ANY}.
- * <p>
- * If the item has visible details, its size will also be taken into
- * account.
- *
- * @param itemId
- * id of item to scroll to.
- * @throws IllegalArgumentException
- * if the provided id is not recognized by the data source.
- */
- public void scrollTo(Object itemId) throws IllegalArgumentException {
- scrollTo(itemId, ScrollDestination.ANY);
- }
-
- /**
- * Scrolls to a certain item, using user-specified scroll destination.
- * <p>
- * If the item has visible details, its size will also be taken into
- * account.
- *
- * @param itemId
- * id of item to scroll to.
- * @param destination
- * value specifying desired position of scrolled-to row.
- * @throws IllegalArgumentException
- * if the provided id is not recognized by the data source.
- */
- public void scrollTo(Object itemId, ScrollDestination destination)
- throws IllegalArgumentException {
-
- int row = datasource.indexOfId(itemId);
-
- if (row == -1) {
- throw new IllegalArgumentException(
- "Item with specified ID does not exist in data source");
- }
-
- GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class);
- clientRPC.scrollToRow(row, destination);
- }
-
- /**
- * Scrolls to the beginning of the first data row.
- */
- public void scrollToStart() {
- GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class);
- clientRPC.scrollToStart();
- }
-
- /**
- * Scrolls to the end of the last data row.
- */
- public void scrollToEnd() {
- GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class);
- clientRPC.scrollToEnd();
- }
-
- /**
- * Sets the number of rows that should be visible in Grid's body, while
- * {@link #getHeightMode()} is {@link HeightMode#ROW}.
- * <p>
- * If Grid is currently not in {@link HeightMode#ROW}, the given value is
- * remembered, and applied once the mode is applied.
- *
- * @param rows
- * The height in terms of number of rows displayed in Grid's
- * body. If Grid doesn't contain enough rows, white space is
- * displayed instead. If <code>null</code> is given, then Grid's
- * height is undefined
- * @throws IllegalArgumentException
- * if {@code rows} is zero or less
- * @throws IllegalArgumentException
- * if {@code rows} is {@link Double#isInfinite(double) infinite}
- * @throws IllegalArgumentException
- * if {@code rows} is {@link Double#isNaN(double) NaN}
- */
- public void setHeightByRows(double rows) {
- if (rows <= 0.0d) {
- throw new IllegalArgumentException(
- "More than zero rows must be shown.");
- } else if (Double.isInfinite(rows)) {
- throw new IllegalArgumentException(
- "Grid doesn't support infinite heights");
- } else if (Double.isNaN(rows)) {
- throw new IllegalArgumentException("NaN is not a valid row count");
- }
-
- getState().heightByRows = rows;
- }
-
- /**
- * Gets the amount of rows in Grid's body that are shown, while
- * {@link #getHeightMode()} is {@link HeightMode#ROW}.
- *
- * @return the amount of rows that are being shown in Grid's body
- * @see #setHeightByRows(double)
- */
- public double getHeightByRows() {
- return getState(false).heightByRows;
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * <em>Note:</em> This method will change the widget's size in the browser
- * only if {@link #getHeightMode()} returns {@link HeightMode#CSS}.
- *
- * @see #setHeightMode(HeightMode)
- */
- @Override
- public void setHeight(float height, Unit unit) {
- super.setHeight(height, unit);
- }
-
- /**
- * Defines the mode in which the Grid widget's height is calculated.
- * <p>
- * If {@link HeightMode#CSS} is given, Grid will respect the values given
- * via a {@code setHeight}-method, and behave as a traditional Component.
- * <p>
- * If {@link HeightMode#ROW} is given, Grid will make sure that the body
- * will display as many rows as {@link #getHeightByRows()} defines.
- * <em>Note:</em> If headers/footers are inserted or removed, the widget
- * will resize itself to still display the required amount of rows in its
- * body. It also takes the horizontal scrollbar into account.
- *
- * @param heightMode
- * the mode in to which Grid should be set
- */
- public void setHeightMode(HeightMode heightMode) {
- /*
- * This method is a workaround for the fact that Vaadin re-applies
- * widget dimensions (height/width) on each state change event. The
- * original design was to have setHeight and setHeightByRow be equals,
- * and whichever was called the latest was considered in effect.
- *
- * But, because of Vaadin always calling setHeight on the widget, this
- * approach doesn't work.
- */
-
- getState().heightMode = heightMode;
- }
-
- /**
- * Returns the current {@link HeightMode} the Grid is in.
- * <p>
- * Defaults to {@link HeightMode#CSS}.
- *
- * @return the current HeightMode
- */
- public HeightMode getHeightMode() {
- return getState(false).heightMode;
- }
-
- /* Selection related methods: */
-
- /**
- * Takes a new {@link SelectionModel} into use.
- * <p>
- * The SelectionModel that is previously in use will have all its items
- * deselected.
- * <p>
- * If the given SelectionModel is already in use, this method does nothing.
- *
- * @param selectionModel
- * the new SelectionModel to use
- * @throws IllegalArgumentException
- * if {@code selectionModel} is <code>null</code>
- */
- public void setSelectionModel(SelectionModel selectionModel)
- throws IllegalArgumentException {
- if (selectionModel == null) {
- throw new IllegalArgumentException(
- "Selection model may not be null");
- }
-
- if (this.selectionModel != selectionModel) {
- // this.selectionModel is null on init
- if (this.selectionModel != null) {
- this.selectionModel.remove();
- }
-
- this.selectionModel = selectionModel;
- selectionModel.setGrid(this);
- }
- }
-
- /**
- * Returns the currently used {@link SelectionModel}.
- *
- * @return the currently used SelectionModel
- */
- public SelectionModel getSelectionModel() {
- return selectionModel;
- }
-
- /**
- * Sets the Grid's selection mode.
- * <p>
- * Grid supports three selection modes: multiselect, single select and no
- * selection, and this is a convenience method for choosing between one of
- * them.
- * <p>
- * Technically, this method is a shortcut that can be used instead of
- * calling {@code setSelectionModel} with a specific SelectionModel
- * instance. Grid comes with three built-in SelectionModel classes, and the
- * {@link SelectionMode} enum represents each of them.
- * <p>
- * Essentially, the two following method calls are equivalent:
- * <p>
- * <code><pre>
- * grid.setSelectionMode(SelectionMode.MULTI);
- * grid.setSelectionModel(new MultiSelectionMode());
- * </pre></code>
- *
- *
- * @param selectionMode
- * the selection mode to switch to
- * @return The {@link SelectionModel} instance that was taken into use
- * @throws IllegalArgumentException
- * if {@code selectionMode} is <code>null</code>
- * @see SelectionModel
- */
- public SelectionModel setSelectionMode(final SelectionMode selectionMode)
- throws IllegalArgumentException {
- if (selectionMode == null) {
- throw new IllegalArgumentException(
- "selection mode may not be null");
- }
- final SelectionModel newSelectionModel = selectionMode.createModel();
- setSelectionModel(newSelectionModel);
- return newSelectionModel;
- }
-
- /**
- * Checks whether an item is selected or not.
- *
- * @param itemId
- * the item id to check for
- * @return <code>true</code> iff the item is selected
- */
- // keep this javadoc in sync with SelectionModel.isSelected
- public boolean isSelected(Object itemId) {
- return selectionModel.isSelected(itemId);
- }
-
- /**
- * Returns a collection of all the currently selected itemIds.
- * <p>
- * This method is a shorthand that delegates to the
- * {@link #getSelectionModel() selection model}.
- *
- * @return a collection of all the currently selected itemIds
- */
- // keep this javadoc in sync with SelectionModel.getSelectedRows
- public Collection<Object> getSelectedRows() {
- return getSelectionModel().getSelectedRows();
- }
-
- /**
- * Gets the item id of the currently selected item.
- * <p>
- * This method is a shorthand that delegates to the
- * {@link #getSelectionModel() selection model}. Only
- * {@link SelectionModel.Single} is supported.
- *
- * @return the item id of the currently selected item, or <code>null</code>
- * if nothing is selected
- * @throws IllegalStateException
- * if the selection model does not implement
- * {@code SelectionModel.Single}
- */
- // keep this javadoc in sync with SelectionModel.Single.getSelectedRow
- public Object getSelectedRow() throws IllegalStateException {
- if (selectionModel instanceof SelectionModel.Single) {
- return ((SelectionModel.Single) selectionModel).getSelectedRow();
- } else if (selectionModel instanceof SelectionModel.Multi) {
- throw new IllegalStateException("Cannot get unique selected row: "
- + "Grid is in multiselect mode "
- + "(the current selection model is "
- + selectionModel.getClass().getName() + ").");
- } else if (selectionModel instanceof SelectionModel.None) {
- throw new IllegalStateException(
- "Cannot get selected row: " + "Grid selection is disabled "
- + "(the current selection model is "
- + selectionModel.getClass().getName() + ").");
- } else {
- throw new IllegalStateException("Cannot get selected row: "
- + "Grid selection model does not implement "
- + SelectionModel.Single.class.getName() + " or "
- + SelectionModel.Multi.class.getName()
- + "(the current model is "
- + selectionModel.getClass().getName() + ").");
- }
- }
-
- /**
- * Marks an item as selected.
- * <p>
- * This method is a shorthand that delegates to the
- * {@link #getSelectionModel() selection model}. Only
- * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are
- * supported.
- *
- * @param itemId
- * the itemId to mark as selected
- * @return <code>true</code> if the selection state changed,
- * <code>false</code> if the itemId already was selected
- * @throws IllegalArgumentException
- * if the {@code itemId} doesn't exist in the currently active
- * Container
- * @throws IllegalStateException
- * if the selection was illegal. One such reason might be that
- * the implementation already had an item selected, and that
- * needs to be explicitly deselected before re-selecting
- * something.
- * @throws IllegalStateException
- * if the selection model does not implement
- * {@code SelectionModel.Single} or {@code SelectionModel.Multi}
- */
- // keep this javadoc in sync with SelectionModel.Single.select
- public boolean select(Object itemId)
- throws IllegalArgumentException, IllegalStateException {
- if (selectionModel instanceof SelectionModel.Single) {
- return ((SelectionModel.Single) selectionModel).select(itemId);
- } else if (selectionModel instanceof SelectionModel.Multi) {
- return ((SelectionModel.Multi) selectionModel).select(itemId);
- } else if (selectionModel instanceof SelectionModel.None) {
- throw new IllegalStateException("Cannot select row '" + itemId
- + "': Grid selection is disabled "
- + "(the current selection model is "
- + selectionModel.getClass().getName() + ").");
- } else {
- throw new IllegalStateException("Cannot select row '" + itemId
- + "': Grid selection model does not implement "
- + SelectionModel.Single.class.getName() + " or "
- + SelectionModel.Multi.class.getName()
- + "(the current model is "
- + selectionModel.getClass().getName() + ").");
- }
- }
-
- /**
- * Marks an item as unselected.
- * <p>
- * This method is a shorthand that delegates to the
- * {@link #getSelectionModel() selection model}. Only
- * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are
- * supported.
- *
- * @param itemId
- * the itemId to remove from being selected
- * @return <code>true</code> if the selection state changed,
- * <code>false</code> if the itemId was already selected
- * @throws IllegalArgumentException
- * if the {@code itemId} doesn't exist in the currently active
- * Container
- * @throws IllegalStateException
- * if the deselection was illegal. One such reason might be that
- * the implementation requires one or more items to be selected
- * at all times.
- * @throws IllegalStateException
- * if the selection model does not implement
- * {@code SelectionModel.Single} or {code SelectionModel.Multi}
- */
- // keep this javadoc in sync with SelectionModel.Single.deselect
- public boolean deselect(Object itemId) throws IllegalStateException {
- if (selectionModel instanceof SelectionModel.Single) {
- if (isSelected(itemId)) {
- return ((SelectionModel.Single) selectionModel).select(null);
- }
- return false;
- } else if (selectionModel instanceof SelectionModel.Multi) {
- return ((SelectionModel.Multi) selectionModel).deselect(itemId);
- } else if (selectionModel instanceof SelectionModel.None) {
- throw new IllegalStateException("Cannot deselect row '" + itemId
- + "': Grid selection is disabled "
- + "(the current selection model is "
- + selectionModel.getClass().getName() + ").");
- } else {
- throw new IllegalStateException("Cannot deselect row '" + itemId
- + "': Grid selection model does not implement "
- + SelectionModel.Single.class.getName() + " or "
- + SelectionModel.Multi.class.getName()
- + "(the current model is "
- + selectionModel.getClass().getName() + ").");
- }
- }
-
- /**
- * Marks all items as unselected.
- * <p>
- * This method is a shorthand that delegates to the
- * {@link #getSelectionModel() selection model}. Only
- * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are
- * supported.
- *
- * @return <code>true</code> if the selection state changed,
- * <code>false</code> if the itemId was already selected
- * @throws IllegalStateException
- * if the deselection was illegal. One such reason might be that
- * the implementation requires one or more items to be selected
- * at all times.
- * @throws IllegalStateException
- * if the selection model does not implement
- * {@code SelectionModel.Single} or {code SelectionModel.Multi}
- */
- public boolean deselectAll() throws IllegalStateException {
- if (selectionModel instanceof SelectionModel.Single) {
- if (getSelectedRow() != null) {
- return deselect(getSelectedRow());
- }
- return false;
- } else if (selectionModel instanceof SelectionModel.Multi) {
- return ((SelectionModel.Multi) selectionModel).deselectAll();
- } else if (selectionModel instanceof SelectionModel.None) {
- throw new IllegalStateException(
- "Cannot deselect all rows" + ": Grid selection is disabled "
- + "(the current selection model is "
- + selectionModel.getClass().getName() + ").");
- } else {
- throw new IllegalStateException("Cannot deselect all rows:"
- + " Grid selection model does not implement "
- + SelectionModel.Single.class.getName() + " or "
- + SelectionModel.Multi.class.getName()
- + "(the current model is "
- + selectionModel.getClass().getName() + ").");
- }
- }
-
- /**
- * Fires a selection change event.
- * <p>
- * <strong>Note:</strong> This is not a method that should be called by
- * application logic. This method is publicly accessible only so that
- * {@link SelectionModel SelectionModels} would be able to inform Grid of
- * these events.
- *
- * @param newSelection
- * the selection that was added by this event
- * @param oldSelection
- * the selection that was removed by this event
- */
- public void fireSelectionEvent(Collection<Object> oldSelection,
- Collection<Object> newSelection) {
- fireEvent(new SelectionEvent(this, oldSelection, newSelection));
- }
-
- @Override
- public void addSelectionListener(SelectionListener listener) {
- addListener(SelectionEvent.class, listener, SELECTION_CHANGE_METHOD);
- }
-
- @Override
- public void removeSelectionListener(SelectionListener listener) {
- removeListener(SelectionEvent.class, listener, SELECTION_CHANGE_METHOD);
- }
-
- private void fireColumnReorderEvent(boolean userOriginated) {
- fireEvent(new ColumnReorderEvent(this, userOriginated));
- }
-
- /**
- * Registers a new column reorder listener.
- *
- * @since 7.5.0
- * @param listener
- * the listener to register
- */
- public void addColumnReorderListener(ColumnReorderListener listener) {
- addListener(ColumnReorderEvent.class, listener, COLUMN_REORDER_METHOD);
- }
-
- /**
- * Removes a previously registered column reorder listener.
- *
- * @since 7.5.0
- * @param listener
- * the listener to remove
- */
- public void removeColumnReorderListener(ColumnReorderListener listener) {
- removeListener(ColumnReorderEvent.class, listener,
- COLUMN_REORDER_METHOD);
- }
-
- private void fireColumnResizeEvent(Column column, boolean userOriginated) {
- fireEvent(new ColumnResizeEvent(this, column, userOriginated));
- }
-
- /**
- * Registers a new column resize listener.
- *
- * @param listener
- * the listener to register
- */
- public void addColumnResizeListener(ColumnResizeListener listener) {
- addListener(ColumnResizeEvent.class, listener, COLUMN_RESIZE_METHOD);
- }
-
- /**
- * Removes a previously registered column resize listener.
- *
- * @param listener
- * the listener to remove
- */
- public void removeColumnResizeListener(ColumnResizeListener listener) {
- removeListener(ColumnResizeEvent.class, listener, COLUMN_RESIZE_METHOD);
- }
-
- /**
- * Gets the {@link KeyMapper } being used by the data source.
- *
- * @return the key mapper being used by the data source
- */
- KeyMapper<Object> getKeyMapper() {
- return datasourceExtension.getKeyMapper();
- }
-
- /**
- * Adds a renderer to this grid's connector hierarchy.
- *
- * @param renderer
- * the renderer to add
- */
- void addRenderer(Renderer<?> renderer) {
- addExtension(renderer);
- }
-
- /**
- * Sets the current sort order using the fluid Sort API. Read the
- * documentation for {@link Sort} for more information.
- * <p>
- * <em>Note:</em> Sorting by a property that has no column in Grid will hide
- * all possible sorting indicators.
- *
- * @param s
- * a sort instance
- *
- * @throws IllegalStateException
- * if container is not sortable (does not implement
- * Container.Sortable)
- * @throws IllegalArgumentException
- * if trying to sort by non-existing property
- */
- public void sort(Sort s) {
- setSortOrder(s.build());
- }
-
- /**
- * Sort this Grid in ascending order by a specified property.
- * <p>
- * <em>Note:</em> Sorting by a property that has no column in Grid will hide
- * all possible sorting indicators.
- *
- * @param propertyId
- * a property ID
- *
- * @throws IllegalStateException
- * if container is not sortable (does not implement
- * Container.Sortable)
- * @throws IllegalArgumentException
- * if trying to sort by non-existing property
- */
- public void sort(Object propertyId) {
- sort(propertyId, SortDirection.ASCENDING);
- }
-
- /**
- * Sort this Grid in user-specified {@link SortOrder} by a property.
- * <p>
- * <em>Note:</em> Sorting by a property that has no column in Grid will hide
- * all possible sorting indicators.
- *
- * @param propertyId
- * a property ID
- * @param direction
- * a sort order value (ascending/descending)
- *
- * @throws IllegalStateException
- * if container is not sortable (does not implement
- * Container.Sortable)
- * @throws IllegalArgumentException
- * if trying to sort by non-existing property
- */
- public void sort(Object propertyId, SortDirection direction) {
- sort(Sort.by(propertyId, direction));
- }
-
- /**
- * Clear the current sort order, and re-sort the grid.
- */
- public void clearSortOrder() {
- sortOrder.clear();
- sort(false);
- }
-
- /**
- * Sets the sort order to use.
- * <p>
- * <em>Note:</em> Sorting by a property that has no column in Grid will hide
- * all possible sorting indicators.
- *
- * @param order
- * a sort order list.
- *
- * @throws IllegalStateException
- * if container is not sortable (does not implement
- * Container.Sortable)
- * @throws IllegalArgumentException
- * if order is null or trying to sort by non-existing property
- */
- public void setSortOrder(List<SortOrder> order) {
- setSortOrder(order, false);
- }
-
- private void setSortOrder(List<SortOrder> order, boolean userOriginated)
- throws IllegalStateException, IllegalArgumentException {
- if (!(getContainerDataSource() instanceof Container.Sortable)) {
- throw new IllegalStateException(
- "Attached container is not sortable (does not implement Container.Sortable)");
- }
-
- if (order == null) {
- throw new IllegalArgumentException("Order list may not be null!");
- }
-
- sortOrder.clear();
-
- Collection<?> sortableProps = ((Container.Sortable) getContainerDataSource())
- .getSortableContainerPropertyIds();
-
- for (SortOrder o : order) {
- if (!sortableProps.contains(o.getPropertyId())) {
- throw new IllegalArgumentException("Property "
- + o.getPropertyId()
- + " does not exist or is not sortable in the current container");
- }
- }
-
- sortOrder.addAll(order);
- sort(userOriginated);
- }
-
- /**
- * Get the current sort order list.
- *
- * @return a sort order list
- */
- public List<SortOrder> getSortOrder() {
- return Collections.unmodifiableList(sortOrder);
- }
-
- /**
- * Apply sorting to data source.
- */
- private void sort(boolean userOriginated) {
-
- Container c = getContainerDataSource();
- if (c instanceof Container.Sortable) {
- Container.Sortable cs = (Container.Sortable) c;
-
- final int items = sortOrder.size();
- Object[] propertyIds = new Object[items];
- boolean[] directions = new boolean[items];
-
- SortDirection[] stateDirs = new SortDirection[items];
-
- for (int i = 0; i < items; ++i) {
- SortOrder order = sortOrder.get(i);
-
- stateDirs[i] = order.getDirection();
- propertyIds[i] = order.getPropertyId();
- switch (order.getDirection()) {
- case ASCENDING:
- directions[i] = true;
- break;
- case DESCENDING:
- directions[i] = false;
- break;
- default:
- throw new IllegalArgumentException("getDirection() of "
- + order + " returned an unexpected value");
- }
- }
-
- cs.sort(propertyIds, directions);
-
- if (columns.keySet().containsAll(Arrays.asList(propertyIds))) {
- String[] columnKeys = new String[items];
- for (int i = 0; i < items; ++i) {
- columnKeys[i] = this.columnKeys.key(propertyIds[i]);
- }
- getState().sortColumns = columnKeys;
- getState(false).sortDirs = stateDirs;
- } else {
- // Not all sorted properties are in Grid. Remove any indicators.
- getState().sortColumns = new String[] {};
- getState(false).sortDirs = new SortDirection[] {};
- }
- fireEvent(new SortEvent(this, new ArrayList<>(sortOrder),
- userOriginated));
- } else {
- throw new IllegalStateException(
- "Container is not sortable (does not implement Container.Sortable)");
- }
- }
-
- /**
- * Adds a sort order change listener that gets notified when the sort order
- * changes.
- *
- * @param listener
- * the sort order change listener to add
- */
- @Override
- public void addSortListener(SortListener listener) {
- addListener(SortEvent.class, listener, SORT_ORDER_CHANGE_METHOD);
- }
-
- /**
- * Removes a sort order change listener previously added using
- * {@link #addSortListener(SortListener)}.
- *
- * @param listener
- * the sort order change listener to remove
- */
- @Override
- public void removeSortListener(SortListener listener) {
- removeListener(SortEvent.class, listener, SORT_ORDER_CHANGE_METHOD);
- }
-
- /* Grid Headers */
-
- /**
- * Returns the header section of this grid. The default header contains a
- * single row displaying the column captions.
- *
- * @return the header
- */
- protected Header getHeader() {
- return header;
- }
-
- /**
- * Gets the header row at given index.
- *
- * @param rowIndex
- * 0 based index for row. Counted from top to bottom
- * @return header row at given index
- * @throws IllegalArgumentException
- * if no row exists at given index
- */
- public HeaderRow getHeaderRow(int rowIndex) {
- return header.getRow(rowIndex);
- }
-
- /**
- * Inserts a new row at the given position to the header section. Shifts the
- * row currently at that position and any subsequent rows down (adds one to
- * their indices).
- *
- * @param index
- * the position at which to insert the row
- * @return the new row
- *
- * @throws IllegalArgumentException
- * if the index is less than 0 or greater than row count
- * @see #appendHeaderRow()
- * @see #prependHeaderRow()
- * @see #removeHeaderRow(HeaderRow)
- * @see #removeHeaderRow(int)
- */
- public HeaderRow addHeaderRowAt(int index) {
- return header.addRowAt(index);
- }
-
- /**
- * Adds a new row at the bottom of the header section.
- *
- * @return the new row
- * @see #prependHeaderRow()
- * @see #addHeaderRowAt(int)
- * @see #removeHeaderRow(HeaderRow)
- * @see #removeHeaderRow(int)
- */
- public HeaderRow appendHeaderRow() {
- return header.appendRow();
- }
-
- /**
- * Returns the current default row of the header section. The default row is
- * a special header row providing a user interface for sorting columns.
- * Setting a header text for column updates cells in the default header.
- *
- * @return the default row or null if no default row set
- */
- public HeaderRow getDefaultHeaderRow() {
- return header.getDefaultRow();
- }
-
- /**
- * Gets the row count for the header section.
- *
- * @return row count
- */
- public int getHeaderRowCount() {
- return header.getRowCount();
- }
-
- /**
- * Adds a new row at the top of the header section.
- *
- * @return the new row
- * @see #appendHeaderRow()
- * @see #addHeaderRowAt(int)
- * @see #removeHeaderRow(HeaderRow)
- * @see #removeHeaderRow(int)
- */
- public HeaderRow prependHeaderRow() {
- return header.prependRow();
- }
-
- /**
- * Removes the given row from the header section.
- *
- * @param row
- * the row to be removed
- *
- * @throws IllegalArgumentException
- * if the row does not exist in this section
- * @see #removeHeaderRow(int)
- * @see #addHeaderRowAt(int)
- * @see #appendHeaderRow()
- * @see #prependHeaderRow()
- */
- public void removeHeaderRow(HeaderRow row) {
- header.removeRow(row);
- }
-
- /**
- * Removes the row at the given position from the header section.
- *
- * @param rowIndex
- * the position of the row
- *
- * @throws IllegalArgumentException
- * if no row exists at given index
- * @see #removeHeaderRow(HeaderRow)
- * @see #addHeaderRowAt(int)
- * @see #appendHeaderRow()
- * @see #prependHeaderRow()
- */
- public void removeHeaderRow(int rowIndex) {
- header.removeRow(rowIndex);
- }
-
- /**
- * Sets the default row of the header. The default row is a special header
- * row providing a user interface for sorting columns.
- *
- * @param row
- * the new default row, or null for no default row
- *
- * @throws IllegalArgumentException
- * header does not contain the row
- */
- public void setDefaultHeaderRow(HeaderRow row) {
- header.setDefaultRow(row);
- }
-
- /**
- * Sets the visibility of the header section.
- *
- * @param visible
- * true to show header section, false to hide
- */
- public void setHeaderVisible(boolean visible) {
- header.setVisible(visible);
- }
-
- /**
- * Returns the visibility of the header section.
- *
- * @return true if visible, false otherwise.
- */
- public boolean isHeaderVisible() {
- return header.isVisible();
- }
-
- /* Grid Footers */
-
- /**
- * Returns the footer section of this grid. The default header contains a
- * single row displaying the column captions.
- *
- * @return the footer
- */
- protected Footer getFooter() {
- return footer;
- }
-
- /**
- * Gets the footer row at given index.
- *
- * @param rowIndex
- * 0 based index for row. Counted from top to bottom
- * @return footer row at given index
- * @throws IllegalArgumentException
- * if no row exists at given index
- */
- public FooterRow getFooterRow(int rowIndex) {
- return footer.getRow(rowIndex);
- }
-
- /**
- * Inserts a new row at the given position to the footer section. Shifts the
- * row currently at that position and any subsequent rows down (adds one to
- * their indices).
- *
- * @param index
- * the position at which to insert the row
- * @return the new row
- *
- * @throws IllegalArgumentException
- * if the index is less than 0 or greater than row count
- * @see #appendFooterRow()
- * @see #prependFooterRow()
- * @see #removeFooterRow(FooterRow)
- * @see #removeFooterRow(int)
- */
- public FooterRow addFooterRowAt(int index) {
- return footer.addRowAt(index);
- }
-
- /**
- * Adds a new row at the bottom of the footer section.
- *
- * @return the new row
- * @see #prependFooterRow()
- * @see #addFooterRowAt(int)
- * @see #removeFooterRow(FooterRow)
- * @see #removeFooterRow(int)
- */
- public FooterRow appendFooterRow() {
- return footer.appendRow();
- }
-
- /**
- * Gets the row count for the footer.
- *
- * @return row count
- */
- public int getFooterRowCount() {
- return footer.getRowCount();
- }
-
- /**
- * Adds a new row at the top of the footer section.
- *
- * @return the new row
- * @see #appendFooterRow()
- * @see #addFooterRowAt(int)
- * @see #removeFooterRow(FooterRow)
- * @see #removeFooterRow(int)
- */
- public FooterRow prependFooterRow() {
- return footer.prependRow();
- }
-
- /**
- * Removes the given row from the footer section.
- *
- * @param row
- * the row to be removed
- *
- * @throws IllegalArgumentException
- * if the row does not exist in this section
- * @see #removeFooterRow(int)
- * @see #addFooterRowAt(int)
- * @see #appendFooterRow()
- * @see #prependFooterRow()
- */
- public void removeFooterRow(FooterRow row) {
- footer.removeRow(row);
- }
-
- /**
- * Removes the row at the given position from the footer section.
- *
- * @param rowIndex
- * the position of the row
- *
- * @throws IllegalArgumentException
- * if no row exists at given index
- * @see #removeFooterRow(FooterRow)
- * @see #addFooterRowAt(int)
- * @see #appendFooterRow()
- * @see #prependFooterRow()
- */
- public void removeFooterRow(int rowIndex) {
- footer.removeRow(rowIndex);
- }
-
- /**
- * Sets the visibility of the footer section.
- *
- * @param visible
- * true to show footer section, false to hide
- */
- public void setFooterVisible(boolean visible) {
- footer.setVisible(visible);
- }
-
- /**
- * Returns the visibility of the footer section.
- *
- * @return true if visible, false otherwise.
- */
- public boolean isFooterVisible() {
- return footer.isVisible();
- }
-
- private void addComponent(Component c) {
- extensionComponents.add(c);
- c.setParent(this);
- markAsDirty();
- }
-
- private void removeComponent(Component c) {
- extensionComponents.remove(c);
- c.setParent(null);
- markAsDirty();
- }
-
- @Override
- public Iterator<Component> iterator() {
- // This is a hash set to avoid adding header/footer components inside
- // merged cells multiple times
- LinkedHashSet<Component> componentList = new LinkedHashSet<>();
-
- Header header = getHeader();
- for (int i = 0; i < header.getRowCount(); ++i) {
- HeaderRow row = header.getRow(i);
- for (Object propId : columns.keySet()) {
- HeaderCell cell = row.getCell(propId);
- if (cell.getCellState().type == GridStaticCellType.WIDGET) {
- componentList.add(cell.getComponent());
- }
- }
- }
-
- Footer footer = getFooter();
- for (int i = 0; i < footer.getRowCount(); ++i) {
- FooterRow row = footer.getRow(i);
- for (Object propId : columns.keySet()) {
- FooterCell cell = row.getCell(propId);
- if (cell.getCellState().type == GridStaticCellType.WIDGET) {
- componentList.add(cell.getComponent());
- }
- }
- }
-
- componentList.addAll(getEditorFields());
-
- componentList.addAll(extensionComponents);
-
- return componentList.iterator();
- }
-
- @Override
- public boolean isRendered(Component childComponent) {
- if (getEditorFields().contains(childComponent)) {
- // Only render editor fields if the editor is open
- return isEditorActive();
- } else {
- // TODO Header and footer components should also only be rendered if
- // the header/footer is visible
- return true;
- }
- }
-
- EditorClientRpc getEditorRpc() {
- return getRpcProxy(EditorClientRpc.class);
- }
-
- /**
- * Sets the {@code CellDescriptionGenerator} instance for generating
- * optional descriptions (tooltips) for individual Grid cells. If a
- * {@link RowDescriptionGenerator} is also set, the row description it
- * generates is displayed for cells for which {@code generator} returns
- * null.
- *
- * @param generator
- * the description generator to use or {@code null} to remove a
- * previously set generator if any
- *
- * @see #setRowDescriptionGenerator(RowDescriptionGenerator)
- *
- * @since 7.6
- */
- public void setCellDescriptionGenerator(
- CellDescriptionGenerator generator) {
- cellDescriptionGenerator = generator;
- getState().hasDescriptions = (generator != null
- || rowDescriptionGenerator != null);
- datasourceExtension.refreshCache();
- }
-
- /**
- * Returns the {@code CellDescriptionGenerator} instance used to generate
- * descriptions (tooltips) for Grid cells.
- *
- * @return the description generator or {@code null} if no generator is set
- *
- * @since 7.6
- */
- public CellDescriptionGenerator getCellDescriptionGenerator() {
- return cellDescriptionGenerator;
- }
-
- /**
- * Sets the {@code RowDescriptionGenerator} instance for generating optional
- * descriptions (tooltips) for Grid rows. If a
- * {@link CellDescriptionGenerator} is also set, the row description
- * generated by {@code generator} is used for cells for which the cell
- * description generator returns null.
- *
- *
- * @param generator
- * the description generator to use or {@code null} to remove a
- * previously set generator if any
- *
- * @see #setCellDescriptionGenerator(CellDescriptionGenerator)
- *
- * @since 7.6
- */
- public void setRowDescriptionGenerator(RowDescriptionGenerator generator) {
- rowDescriptionGenerator = generator;
- getState().hasDescriptions = (generator != null
- || cellDescriptionGenerator != null);
- datasourceExtension.refreshCache();
- }
-
- /**
- * Returns the {@code RowDescriptionGenerator} instance used to generate
- * descriptions (tooltips) for Grid rows
- *
- * @return the description generator or {@code} null if no generator is set
- *
- * @since 7.6
- */
- public RowDescriptionGenerator getRowDescriptionGenerator() {
- return rowDescriptionGenerator;
- }
-
- /**
- * Sets the style generator that is used for generating styles for cells
- *
- * @param cellStyleGenerator
- * the cell style generator to set, or <code>null</code> to
- * remove a previously set generator
- */
- public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) {
- this.cellStyleGenerator = cellStyleGenerator;
- datasourceExtension.refreshCache();
- }
-
- /**
- * Gets the style generator that is used for generating styles for cells
- *
- * @return the cell style generator, or <code>null</code> if no generator is
- * set
- */
- public CellStyleGenerator getCellStyleGenerator() {
- return cellStyleGenerator;
- }
-
- /**
- * Sets the style generator that is used for generating styles for rows
- *
- * @param rowStyleGenerator
- * the row style generator to set, or <code>null</code> to remove
- * a previously set generator
- */
- public void setRowStyleGenerator(RowStyleGenerator rowStyleGenerator) {
- this.rowStyleGenerator = rowStyleGenerator;
- datasourceExtension.refreshCache();
- }
-
- /**
- * Gets the style generator that is used for generating styles for rows
- *
- * @return the row style generator, or <code>null</code> if no generator is
- * set
- */
- public RowStyleGenerator getRowStyleGenerator() {
- return rowStyleGenerator;
- }
-
- /**
- * Adds a row to the underlying container. The order of the parameters
- * should match the current visible column order.
- * <p>
- * Please note that it's generally only safe to use this method during
- * initialization. After Grid has been initialized and the visible column
- * order might have been changed, it's better to instead add items directly
- * to the underlying container and use {@link Item#getItemProperty(Object)}
- * to make sure each value is assigned to the intended property.
- *
- * @param values
- * the cell values of the new row, in the same order as the
- * visible column order, not <code>null</code>.
- * @return the item id of the new row
- * @throws IllegalArgumentException
- * if values is null
- * @throws IllegalArgumentException
- * if its length does not match the number of visible columns
- * @throws IllegalArgumentException
- * if a parameter value is not an instance of the corresponding
- * property type
- * @throws UnsupportedOperationException
- * if the container does not support adding new items
- */
- public Object addRow(Object... values) {
- if (values == null) {
- throw new IllegalArgumentException("Values cannot be null");
- }
-
- Indexed dataSource = getContainerDataSource();
- List<String> columnOrder = getState(false).columnOrder;
-
- if (values.length != columnOrder.size()) {
- throw new IllegalArgumentException(
- "There are " + columnOrder.size() + " visible columns, but "
- + values.length + " cell values were provided.");
- }
-
- // First verify all parameter types
- for (int i = 0; i < columnOrder.size(); i++) {
- Object propertyId = getPropertyIdByColumnId(columnOrder.get(i));
-
- Class<?> propertyType = dataSource.getType(propertyId);
- if (values[i] != null && !propertyType.isInstance(values[i])) {
- throw new IllegalArgumentException("Parameter " + i + "("
- + values[i] + ") is not an instance of "
- + propertyType.getCanonicalName());
- }
- }
-
- Object itemId = dataSource.addItem();
- try {
- Item item = dataSource.getItem(itemId);
- for (int i = 0; i < columnOrder.size(); i++) {
- Object propertyId = getPropertyIdByColumnId(columnOrder.get(i));
- Property<Object> property = item.getItemProperty(propertyId);
- property.setValue(values[i]);
- }
- } catch (RuntimeException e) {
- try {
- dataSource.removeItem(itemId);
- } catch (Exception e2) {
- getLogger().log(Level.SEVERE,
- "Error recovering from exception in addRow", e);
- }
- throw e;
- }
-
- return itemId;
- }
-
- private static Logger getLogger() {
- return Logger.getLogger(LegacyGrid.class.getName());
- }
-
- /**
- * Sets whether or not the item editor UI is enabled for this grid. When the
- * editor is enabled, the user can open it by double-clicking a row or
- * hitting enter when a row is focused. The editor can also be opened
- * programmatically using the {@link #editItem(Object)} method.
- *
- * @param isEnabled
- * <code>true</code> to enable the feature, <code>false</code>
- * otherwise
- * @throws IllegalStateException
- * if an item is currently being edited
- *
- * @see #getEditedItemId()
- */
- public void setEditorEnabled(boolean isEnabled)
- throws IllegalStateException {
- if (isEditorActive()) {
- throw new IllegalStateException(
- "Cannot disable the editor while an item ("
- + getEditedItemId() + ") is being edited");
- }
- if (isEditorEnabled() != isEnabled) {
- getState().editorEnabled = isEnabled;
- }
- }
-
- /**
- * Checks whether the item editor UI is enabled for this grid.
- *
- * @return <code>true</code> iff the editor is enabled for this grid
- *
- * @see #setEditorEnabled(boolean)
- * @see #getEditedItemId()
- */
- public boolean isEditorEnabled() {
- return getState(false).editorEnabled;
- }
-
- /**
- * Gets the id of the item that is currently being edited.
- *
- * @return the id of the item that is currently being edited, or
- * <code>null</code> if no item is being edited at the moment
- */
- public Object getEditedItemId() {
- return editedItemId;
- }
-
- /**
- * Gets the field group that is backing the item editor of this grid.
- *
- * @return the backing field group
- */
- public FieldGroup getEditorFieldGroup() {
- return editorFieldGroup;
- }
-
- /**
- * Sets the field group that is backing the item editor of this grid.
- *
- * @param fieldGroup
- * the backing field group
- *
- * @throws IllegalStateException
- * if the editor is currently active
- */
- public void setEditorFieldGroup(FieldGroup fieldGroup) {
- if (isEditorActive()) {
- throw new IllegalStateException(
- "Cannot change field group while an item ("
- + getEditedItemId() + ") is being edited");
- }
- editorFieldGroup = fieldGroup;
- }
-
- /**
- * Returns whether an item is currently being edited in the editor.
- *
- * @return true iff the editor is open
- */
- public boolean isEditorActive() {
- return editorActive;
- }
-
- private void checkColumnExists(Object propertyId) {
- if (getColumn(propertyId) == null) {
- throw new IllegalArgumentException(
- "There is no column with the property id " + propertyId);
- }
- }
-
- private LegacyField<?> getEditorField(Object propertyId) {
- checkColumnExists(propertyId);
-
- if (!getColumn(propertyId).isEditable()) {
- return null;
- }
-
- LegacyField<?> editor = editorFieldGroup.getField(propertyId);
-
- try {
- if (editor == null) {
- editor = editorFieldGroup.buildAndBind(propertyId);
- }
- } finally {
- if (editor == null) {
- editor = editorFieldGroup.getField(propertyId);
- }
-
- if (editor != null && editor.getParent() != LegacyGrid.this) {
- assert editor.getParent() == null;
- editor.setParent(this);
- }
- }
- return editor;
- }
-
- /**
- * Opens the editor interface for the provided item. Scrolls the Grid to
- * bring the item to view if it is not already visible.
- *
- * Note that any cell content rendered by a WidgetRenderer will not be
- * visible in the editor row.
- *
- * @param itemId
- * the id of the item to edit
- * @throws IllegalStateException
- * if the editor is not enabled or already editing an item in
- * buffered mode
- * @throws IllegalArgumentException
- * if the {@code itemId} is not in the backing container
- * @see #setEditorEnabled(boolean)
- */
- public void editItem(Object itemId)
- throws IllegalStateException, IllegalArgumentException {
- if (!isEditorEnabled()) {
- throw new IllegalStateException("Item editor is not enabled");
- } else if (isEditorBuffered() && editedItemId != null) {
- throw new IllegalStateException("Editing item " + itemId
- + " failed. Item editor is already editing item "
- + editedItemId);
- } else if (!getContainerDataSource().containsId(itemId)) {
- throw new IllegalArgumentException("Item with id " + itemId
- + " not found in current container");
- }
- editedItemId = itemId;
- getEditorRpc().bind(getContainerDataSource().indexOfId(itemId));
- }
-
- protected void doEditItem() {
- Item item = getContainerDataSource().getItem(editedItemId);
-
- editorFieldGroup.setItemDataSource(item);
-
- for (Column column : getColumns()) {
- column.getState().editorConnector = getEditorField(
- column.getPropertyId());
- }
-
- editorActive = true;
- // Must ensure that all fields, recursively, are sent to the client
- // This is needed because the fields are hidden using isRendered
- for (LegacyField<?> f : getEditorFields()) {
- f.markAsDirtyRecursive();
- }
-
- if (datasource instanceof ItemSetChangeNotifier) {
- ((ItemSetChangeNotifier) datasource)
- .addItemSetChangeListener(editorClosingItemSetListener);
- }
- }
-
- private void setEditorField(Object propertyId, LegacyField<?> field) {
- checkColumnExists(propertyId);
-
- LegacyField<?> oldField = editorFieldGroup.getField(propertyId);
- if (oldField != null) {
- editorFieldGroup.unbind(oldField);
- oldField.setParent(null);
- }
-
- if (field != null) {
- field.setParent(this);
- editorFieldGroup.bind(field, propertyId);
- }
- }
-
- /**
- * Saves all changes done to the bound fields.
- * <p>
- * <em>Note:</em> This is a pass-through call to the backing field group.
- *
- * @throws CommitException
- * If the commit was aborted
- *
- * @see FieldGroup#commit()
- */
- public void saveEditor() throws CommitException {
- editorFieldGroup.commit();
- }
-
- /**
- * Cancels the currently active edit if any. Hides the editor and discards
- * possible unsaved changes in the editor fields.
- */
- public void cancelEditor() {
- if (isEditorActive()) {
- getEditorRpc()
- .cancel(getContainerDataSource().indexOfId(editedItemId));
- doCancelEditor();
- }
- }
-
- protected void doCancelEditor() {
- editedItemId = null;
- editorActive = false;
- editorFieldGroup.discard();
- editorFieldGroup.setItemDataSource(null);
-
- if (datasource instanceof ItemSetChangeNotifier) {
- ((ItemSetChangeNotifier) datasource)
- .removeItemSetChangeListener(editorClosingItemSetListener);
- }
-
- // Mark Grid as dirty so the client side gets to know that the editors
- // are no longer attached
- markAsDirty();
- }
-
- void resetEditor() {
- if (isEditorActive()) {
- /*
- * Simply force cancel the editing; throwing here would just make
- * Grid.setContainerDataSource semantics more complicated.
- */
- cancelEditor();
- }
- for (LegacyField<?> editor : getEditorFields()) {
- editor.setParent(null);
- }
-
- editedItemId = null;
- editorActive = false;
- editorFieldGroup = new CustomFieldGroup();
- }
-
- /**
- * Gets a collection of all fields bound to the item editor of this grid.
- * <p>
- * When {@link #editItem(Object) editItem} is called, fields are
- * automatically created and bound to any unbound properties.
- *
- * @return a collection of all the fields bound to the item editor
- */
- Collection<LegacyField<?>> getEditorFields() {
- Collection<LegacyField<?>> fields = editorFieldGroup.getFields();
- assert allAttached(fields);
- return fields;
- }
-
- private boolean allAttached(Collection<? extends Component> components) {
- for (Component component : components) {
- if (component.getParent() != this) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Sets the field factory for the {@link FieldGroup}. The field factory is
- * only used when {@link FieldGroup} creates a new field.
- * <p>
- * <em>Note:</em> This is a pass-through call to the backing field group.
- *
- * @param fieldFactory
- * The field factory to use
- */
- public void setEditorFieldFactory(FieldGroupFieldFactory fieldFactory) {
- editorFieldGroup.setFieldFactory(fieldFactory);
- }
-
- /**
- * Sets the error handler for the editor.
- *
- * The error handler is called whenever there is an exception in the editor.
- *
- * @param editorErrorHandler
- * The editor error handler to use
- * @throws IllegalArgumentException
- * if the error handler is null
- */
- public void setEditorErrorHandler(EditorErrorHandler editorErrorHandler)
- throws IllegalArgumentException {
- if (editorErrorHandler == null) {
- throw new IllegalArgumentException(
- "The error handler cannot be null");
- }
- this.editorErrorHandler = editorErrorHandler;
- }
-
- /**
- * Gets the error handler used for the editor
- *
- * @see #setErrorHandler(com.vaadin.server.ErrorHandler)
- * @return the editor error handler, never null
- */
- public EditorErrorHandler getEditorErrorHandler() {
- return editorErrorHandler;
- }
-
- /**
- * Gets the field factory for the {@link FieldGroup}. The field factory is
- * only used when {@link FieldGroup} creates a new field.
- * <p>
- * <em>Note:</em> This is a pass-through call to the backing field group.
- *
- * @return The field factory in use
- */
- public FieldGroupFieldFactory getEditorFieldFactory() {
- return editorFieldGroup.getFieldFactory();
- }
-
- /**
- * Sets the caption on the save button in the Grid editor.
- *
- * @param saveCaption
- * the caption to set
- * @throws IllegalArgumentException
- * if {@code saveCaption} is {@code null}
- */
- public void setEditorSaveCaption(String saveCaption)
- throws IllegalArgumentException {
- if (saveCaption == null) {
- throw new IllegalArgumentException("Save caption cannot be null");
- }
- getState().editorSaveCaption = saveCaption;
- }
-
- /**
- * Gets the current caption of the save button in the Grid editor.
- *
- * @return the current caption of the save button
- */
- public String getEditorSaveCaption() {
- return getState(false).editorSaveCaption;
- }
-
- /**
- * Sets the caption on the cancel button in the Grid editor.
- *
- * @param cancelCaption
- * the caption to set
- * @throws IllegalArgumentException
- * if {@code cancelCaption} is {@code null}
- */
- public void setEditorCancelCaption(String cancelCaption)
- throws IllegalArgumentException {
- if (cancelCaption == null) {
- throw new IllegalArgumentException("Cancel caption cannot be null");
- }
- getState().editorCancelCaption = cancelCaption;
- }
-
- /**
- * Gets the current caption of the cancel button in the Grid editor.
- *
- * @return the current caption of the cancel button
- */
- public String getEditorCancelCaption() {
- return getState(false).editorCancelCaption;
- }
-
- /**
- * Sets the buffered editor mode. The default mode is buffered (
- * <code>true</code>).
- *
- * @since 7.6
- * @param editorBuffered
- * <code>true</code> to enable buffered editor,
- * <code>false</code> to disable it
- * @throws IllegalStateException
- * If editor is active while attempting to change the buffered
- * mode.
- */
- public void setEditorBuffered(boolean editorBuffered)
- throws IllegalStateException {
- if (isEditorActive()) {
- throw new IllegalStateException(
- "Can't change editor unbuffered mode while editor is active.");
- }
- getState().editorBuffered = editorBuffered;
- editorFieldGroup.setBuffered(editorBuffered);
- }
-
- /**
- * Gets the buffered editor mode.
- *
- * @since 7.6
- * @return <code>true</code> if buffered editor is enabled,
- * <code>false</code> otherwise
- */
- public boolean isEditorBuffered() {
- return getState(false).editorBuffered;
- }
-
- @Override
- public void addItemClickListener(ItemClickListener listener) {
- addListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
- listener, ItemClickEvent.ITEM_CLICK_METHOD);
- }
-
- @Override
- @Deprecated
- public void addListener(ItemClickListener listener) {
- addItemClickListener(listener);
- }
-
- @Override
- public void removeItemClickListener(ItemClickListener listener) {
- removeListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
- listener);
- }
-
- @Override
- @Deprecated
- public void removeListener(ItemClickListener listener) {
- removeItemClickListener(listener);
- }
-
- /**
- * Requests that the column widths should be recalculated.
- * <p>
- * In most cases Grid will know when column widths need to be recalculated
- * but this method can be used to force recalculation in situations when
- * grid does not recalculate automatically.
- *
- * @since 7.4.1
- */
- public void recalculateColumnWidths() {
- getRpcProxy(GridClientRpc.class).recalculateColumnWidths();
- }
-
- /**
- * Registers a new column visibility change listener
- *
- * @since 7.5.0
- * @param listener
- * the listener to register
- */
- public void addColumnVisibilityChangeListener(
- ColumnVisibilityChangeListener listener) {
- addListener(ColumnVisibilityChangeEvent.class, listener,
- COLUMN_VISIBILITY_METHOD);
- }
-
- /**
- * Removes a previously registered column visibility change listener
- *
- * @since 7.5.0
- * @param listener
- * the listener to remove
- */
- public void removeColumnVisibilityChangeListener(
- ColumnVisibilityChangeListener listener) {
- removeListener(ColumnVisibilityChangeEvent.class, listener,
- COLUMN_VISIBILITY_METHOD);
- }
-
- private void fireColumnVisibilityChangeEvent(Column column, boolean hidden,
- boolean isUserOriginated) {
- fireEvent(new ColumnVisibilityChangeEvent(this, column, hidden,
- isUserOriginated));
- }
-
- /**
- * Sets a new details generator for row details.
- * <p>
- * The currently opened row details will be re-rendered.
- *
- * @since 7.5.0
- * @param detailsGenerator
- * the details generator to set
- * @throws IllegalArgumentException
- * if detailsGenerator is <code>null</code>;
- */
- public void setDetailsGenerator(DetailsGenerator detailsGenerator)
- throws IllegalArgumentException {
- detailComponentManager.setDetailsGenerator(detailsGenerator);
- }
-
- /**
- * Gets the current details generator for row details.
- *
- * @since 7.5.0
- * @return the detailsGenerator the current details generator
- */
- public DetailsGenerator getDetailsGenerator() {
- return detailComponentManager.getDetailsGenerator();
- }
-
- /**
- * Shows or hides the details for a specific item.
- *
- * @since 7.5.0
- * @param itemId
- * the id of the item for which to set details visibility
- * @param visible
- * <code>true</code> to show the details, or <code>false</code>
- * to hide them
- */
- public void setDetailsVisible(Object itemId, boolean visible) {
- detailComponentManager.setDetailsVisible(itemId, visible);
- }
-
- /**
- * Checks whether details are visible for the given item.
- *
- * @since 7.5.0
- * @param itemId
- * the id of the item for which to check details visibility
- * @return <code>true</code> iff the details are visible
- */
- public boolean isDetailsVisible(Object itemId) {
- return detailComponentManager.isDetailsVisible(itemId);
- }
-
- private static SelectionMode getDefaultSelectionMode() {
- return SelectionMode.SINGLE;
- }
-
- @Override
- public void readDesign(Element design, DesignContext context) {
- super.readDesign(design, context);
-
- Attributes attrs = design.attributes();
- if (attrs.hasKey("editable")) {
- setEditorEnabled(DesignAttributeHandler.readAttribute("editable",
- attrs, boolean.class));
- }
- if (attrs.hasKey("rows")) {
- setHeightByRows(DesignAttributeHandler.readAttribute("rows", attrs,
- double.class));
- setHeightMode(HeightMode.ROW);
- }
- if (attrs.hasKey("selection-mode")) {
- setSelectionMode(DesignAttributeHandler.readAttribute(
- "selection-mode", attrs, SelectionMode.class));
- }
-
- if (design.children().size() > 0) {
- if (design.children().size() > 1
- || !design.child(0).tagName().equals("table")) {
- throw new DesignException(
- "Grid needs to have a table element as its only child");
- }
- Element table = design.child(0);
-
- Elements colgroups = table.getElementsByTag("colgroup");
- if (colgroups.size() != 1) {
- throw new DesignException(
- "Table element in declarative Grid needs to have a"
- + " colgroup defining the columns used in Grid");
- }
-
- int i = 0;
- for (Element col : colgroups.get(0).getElementsByTag("col")) {
- String propertyId = DesignAttributeHandler.readAttribute(
- "property-id", col.attributes(), "property-" + i,
- String.class);
- addColumn(propertyId, String.class).readDesign(col, context);
- ++i;
- }
-
- for (Element child : table.children()) {
- if (child.tagName().equals("thead")) {
- header.readDesign(child, context);
- } else if (child.tagName().equals("tbody")) {
- for (Element row : child.children()) {
- Elements cells = row.children();
- Object[] data = new String[cells.size()];
- for (int c = 0; c < cells.size(); ++c) {
- data[c] = cells.get(c).html();
- }
- addRow(data);
- }
-
- // Since inline data is used, set HTML renderer for columns
- for (Column c : getColumns()) {
- c.setRenderer(new HtmlRenderer());
- }
- } else if (child.tagName().equals("tfoot")) {
- footer.readDesign(child, context);
- }
- }
- }
-
- // Read frozen columns after columns are read.
- if (attrs.hasKey("frozen-columns")) {
- setFrozenColumnCount(DesignAttributeHandler
- .readAttribute("frozen-columns", attrs, int.class));
- }
- }
-
- @Override
- public void writeDesign(Element design, DesignContext context) {
- super.writeDesign(design, context);
-
- Attributes attrs = design.attributes();
- LegacyGrid def = context.getDefaultInstance(this);
-
- DesignAttributeHandler.writeAttribute("editable", attrs,
- isEditorEnabled(), def.isEditorEnabled(), boolean.class);
-
- DesignAttributeHandler.writeAttribute("frozen-columns", attrs,
- getFrozenColumnCount(), def.getFrozenColumnCount(), int.class);
-
- if (getHeightMode() == HeightMode.ROW) {
- DesignAttributeHandler.writeAttribute("rows", attrs,
- getHeightByRows(), def.getHeightByRows(), double.class);
- }
-
- SelectionMode selectionMode = null;
-
- if (selectionModel.getClass().equals(SingleSelectionModel.class)) {
- selectionMode = SelectionMode.SINGLE;
- } else if (selectionModel.getClass()
- .equals(MultiSelectionModel.class)) {
- selectionMode = SelectionMode.MULTI;
- } else if (selectionModel.getClass().equals(NoSelectionModel.class)) {
- selectionMode = SelectionMode.NONE;
- }
-
- assert selectionMode != null : "Unexpected selection model "
- + selectionModel.getClass().getName();
-
- DesignAttributeHandler.writeAttribute("selection-mode", attrs,
- selectionMode, getDefaultSelectionMode(), SelectionMode.class);
-
- if (columns.isEmpty()) {
- // Empty grid. Structure not needed.
- return;
- }
-
- // Do structure.
- Element tableElement = design.appendElement("table");
- Element colGroup = tableElement.appendElement("colgroup");
-
- List<Column> columnOrder = getColumns();
- for (int i = 0; i < columnOrder.size(); ++i) {
- Column column = columnOrder.get(i);
- Element colElement = colGroup.appendElement("col");
- column.writeDesign(colElement, context);
- }
-
- // Always write thead. Reads correctly when there no header rows
- header.writeDesign(tableElement.appendElement("thead"), context);
-
- if (context.shouldWriteData(this)) {
- Element bodyElement = tableElement.appendElement("tbody");
- for (Object itemId : datasource.getItemIds()) {
- Element tableRow = bodyElement.appendElement("tr");
- for (Column c : getColumns()) {
- Object value = datasource.getItem(itemId)
- .getItemProperty(c.getPropertyId()).getValue();
- tableRow.appendElement("td")
- .append((value != null ? DesignFormatter
- .encodeForTextNode(value.toString()) : ""));
- }
- }
- }
-
- if (footer.getRowCount() > 0) {
- footer.writeDesign(tableElement.appendElement("tfoot"), context);
- }
- }
-
- @Override
- protected Collection<String> getCustomAttributes() {
- Collection<String> result = super.getCustomAttributes();
- result.add("editor-enabled");
- result.add("editable");
- result.add("frozen-column-count");
- result.add("frozen-columns");
- result.add("height-by-rows");
- result.add("rows");
- result.add("selection-mode");
- result.add("header-visible");
- result.add("footer-visible");
- result.add("editor-error-handler");
- result.add("height-mode");
-
- return result;
- }
-}