]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add API to access features of SelectionModels (#10939) 10471/head pr10471/r2
authorTeemu Suo-Anttila <tsuoanttila@users.noreply.github.com>
Tue, 4 Sep 2018 12:45:08 +0000 (15:45 +0300)
committerIlia Motornyi <elmot@vaadin.com>
Tue, 4 Sep 2018 12:45:08 +0000 (15:45 +0300)
server/src/main/java/com/vaadin/ui/Grid.java
server/src/main/java/com/vaadin/ui/components/grid/GridMultiSelect.java [new file with mode: 0644]
server/src/main/java/com/vaadin/ui/components/grid/GridSingleSelect.java [new file with mode: 0644]
server/src/test/java/com/vaadin/tests/components/grid/GridSelectionModeTest.java

index 40618074d83633b0aa49f14a4dd764a7d6165971..6e7d7b1cd4fae9dc131ca5b0f07f41f99caa26e0 100644 (file)
@@ -103,7 +103,9 @@ import com.vaadin.ui.components.grid.Editor;
 import com.vaadin.ui.components.grid.EditorImpl;
 import com.vaadin.ui.components.grid.Footer;
 import com.vaadin.ui.components.grid.FooterRow;
+import com.vaadin.ui.components.grid.GridMultiSelect;
 import com.vaadin.ui.components.grid.GridSelectionModel;
+import com.vaadin.ui.components.grid.GridSingleSelect;
 import com.vaadin.ui.components.grid.Header;
 import com.vaadin.ui.components.grid.Header.Row;
 import com.vaadin.ui.components.grid.HeaderCell;
@@ -3904,14 +3906,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
      * @throws IllegalStateException
      *             if not using a single selection model
      */
-    public SingleSelect<T> asSingleSelect() {
-        GridSelectionModel<T> model = getSelectionModel();
-        if (!(model instanceof SingleSelectionModel)) {
-            throw new IllegalStateException(
-                    "Grid is not in single select mode, it needs to be explicitly set to such with setSelectionModel(SingleSelectionModel) before being able to use single selection features.");
-        }
-
-        return ((SingleSelectionModel<T>) model).asSingleSelect();
+    public GridSingleSelect<T> asSingleSelect() {
+        return new GridSingleSelect<>(this);
     }
 
     public Editor<T> getEditor() {
@@ -3928,13 +3924,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
      * @throws IllegalStateException
      *             if not using a multiselection model
      */
-    public MultiSelect<T> asMultiSelect() {
-        GridSelectionModel<T> model = getSelectionModel();
-        if (!(model instanceof MultiSelectionModel)) {
-            throw new IllegalStateException(
-                    "Grid is not in multiselect mode, it needs to be explicitly set to such with setSelectionModel(MultiSelectionModel) before being able to use multiselection features.");
-        }
-        return ((MultiSelectionModel<T>) model).asMultiSelect();
+    public GridMultiSelect<T> asMultiSelect() {
+        return new GridMultiSelect<>(this);
     }
 
     /**
diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridMultiSelect.java b/server/src/main/java/com/vaadin/ui/components/grid/GridMultiSelect.java
new file mode 100644 (file)
index 0000000..bf3c9d0
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2000-2018 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.components.grid;
+
+import java.util.Optional;
+import java.util.Set;
+
+import com.vaadin.event.selection.MultiSelectionListener;
+import com.vaadin.event.selection.SelectionListener;
+import com.vaadin.shared.Registration;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.MultiSelect;
+import com.vaadin.ui.components.grid.MultiSelectionModel.SelectAllCheckBoxVisibility;
+
+/**
+ * Wrapper class to wrap Grid into a MultiSelect. This class also provides
+ * useful access to API of MultiSelectionModel.
+ *
+ * @param <T>
+ *            the bean type of grid
+ * @since
+ */
+public class GridMultiSelect<T> implements MultiSelect<T> {
+
+    private MultiSelectionModel<T> model;
+
+    /**
+     * Constructs a MultiSelect wrapper for given Grid.
+     *
+     * @param grid
+     *            the grid to wrap
+     */
+    public GridMultiSelect(Grid<T> grid) {
+        GridSelectionModel<T> selectionModel = grid.getSelectionModel();
+        if (!(selectionModel instanceof MultiSelectionModel)) {
+            throw new IllegalStateException(
+                    "Grid is not in multiselect mode, it needs to be explicitly set to such with setSelectionModel(MultiSelectionModel) before being able to use multiselection features.");
+        }
+        model = (MultiSelectionModel<T>) selectionModel;
+    }
+
+    /* API for MultiSelectionModelImpl */
+
+    /**
+     * Get first selected data item.
+     *
+     * @return the first selected item.
+     */
+    public Optional<T> getFirstSelectedItem() {
+        return model.getFirstSelectedItem();
+    }
+
+    /**
+     * Selects all available the items.
+     */
+    public void selectAll() {
+        model.selectAll();
+    }
+
+    /**
+     * Selects the given item. Depending on the implementation, may cause other
+     * items to be deselected. If the item is already selected, does nothing.
+     *
+     * @param item
+     *            the item to select, not null
+     */
+    public void deselect(T item) {
+        model.deselect(item);
+    }
+
+    /**
+     * Selects the given item. If another item was already selected, that item
+     * is deselected.
+     * 
+     * @param item
+     *            the item to select
+     */
+    public void select(T item) {
+        model.select(item);
+    }
+
+    /**
+     * Deselects all currently selected items, if any.
+     */
+    public void deselectAll() {
+        model.deselectAll();
+    }
+
+    /**
+     * Adds the given items to the set of currently selected items.
+     * <p>
+     * By default this does not clear any previous selection. To do that, use
+     * {@link #deselectAll()}.
+     * <p>
+     * If the all the items were already selected, this is a NO-OP.
+     * <p>
+     * This is a short-hand for {@link #updateSelection(Set, Set)} with nothing
+     * to deselect.
+     *
+     * @param items
+     *            to add to selection, not {@code null}
+     */
+    public void selectItems(T... items) {
+        model.selectItems(items);
+    }
+
+    /**
+     * Removes the given items from the set of currently selected items.
+     * <p>
+     * If the none of the items were selected, this is a NO-OP.
+     * <p>
+     * This is a short-hand for {@link #updateSelection(Set, Set)} with nothing
+     * to select.
+     *
+     * @param items
+     *            to remove from selection, not {@code null}
+     */
+    public void deselectItems(T... items) {
+        model.deselectItems(items);
+    }
+
+    /**
+     * Sets the select all checkbox visibility mode.
+     * <p>
+     * The default value is {@link SelectAllCheckBoxVisibility#DEFAULT}, which
+     * means that the checkbox is only visible if the grid's data provider is
+     * in- memory.
+     *
+     * @param visibility
+     *            the visiblity mode to use
+     * @see SelectAllCheckBoxVisibility
+     */
+    public void setSelectAllCheckBoxVisibility(
+            SelectAllCheckBoxVisibility visibility) {
+        model.setSelectAllCheckBoxVisibility(visibility);
+    }
+
+    /**
+     * Gets the current mode for the select all checkbox visibility.
+     *
+     * @return the select all checkbox visibility mode
+     * @see SelectAllCheckBoxVisibility
+     * @see #isSelectAllCheckBoxVisible()
+     */
+    public SelectAllCheckBoxVisibility getSelectAllCheckBoxVisibility() {
+        return model.getSelectAllCheckBoxVisibility();
+    }
+
+    /**
+     * Returns whether the select all checkbox will be visible with the current
+     * setting of
+     * {@link #setSelectAllCheckBoxVisibility(SelectAllCheckBoxVisibility)}.
+     *
+     * @return {@code true} if the checkbox will be visible with the current
+     *         settings
+     * @see SelectAllCheckBoxVisibility
+     * @see #setSelectAllCheckBoxVisibility(SelectAllCheckBoxVisibility)
+     */
+    public boolean isSelectAllCheckBoxVisible() {
+        return model.isSelectAllCheckBoxVisible();
+    }
+
+    /**
+     * Sets whether the user is allowed to change the selection.
+     * <p>
+     * The check is done only for the client side actions. It doesn't affect
+     * selection requests sent from the server side.
+     *
+     * @param allowed
+     *            <code>true</code> if the user is allowed to change the
+     *            selection, <code>false</code> otherwise
+     */
+    public void setUserSelectionAllowed(boolean allowed) {
+        model.setUserSelectionAllowed(allowed);
+    }
+
+    /**
+     * Checks if the user is allowed to change the selection.
+     * <p>
+     * The check is done only for the client side actions. It doesn't affect
+     * selection requests sent from the server side.
+     *
+     * @return <code>true</code> if the user is allowed to change the selection,
+     *         <code>false</code> otherwise
+     */
+    public boolean isUserSelectionAllowed() {
+        return model.isUserSelectionAllowed();
+    }
+
+    /**
+     * Adds a generic listener to this selection model, accepting both single
+     * and multiselection events.
+     *
+     * @param listener
+     *            the listener to add
+     * @return a registration handle for removing the listener
+     */
+    public Registration addSelectionListener(SelectionListener<T> listener) {
+        return model.addSelectionListener(listener);
+    }
+
+    /**
+     * Adds a selection listener that will be called when the selection is
+     * changed either by the user or programmatically.
+     *
+     * @param listener
+     *            the value change listener, not {@code null}
+     * @return a registration for the listener
+     */
+    public Registration addMultiSelectionListener(
+            MultiSelectionListener<T> listener) {
+        return model.addMultiSelectionListener(listener);
+    }
+
+    /* MultiSelect implementation */
+
+    @Override
+    public void setValue(Set<T> value) {
+        model.asMultiSelect().setValue(value);
+    }
+
+    @Override
+    public Set<T> getValue() {
+        return model.asMultiSelect().getValue();
+    }
+
+    @Override
+    public Registration addValueChangeListener(
+            ValueChangeListener<Set<T>> listener) {
+        return model.asMultiSelect().addValueChangeListener(listener);
+    }
+
+    @Override
+    public void setRequiredIndicatorVisible(boolean requiredIndicatorVisible) {
+        model.asMultiSelect()
+                .setRequiredIndicatorVisible(requiredIndicatorVisible);
+    }
+
+    @Override
+    public boolean isRequiredIndicatorVisible() {
+        return model.asMultiSelect().isRequiredIndicatorVisible();
+    }
+
+    @Override
+    public void setReadOnly(boolean readOnly) {
+        model.asMultiSelect().setReadOnly(readOnly);
+    }
+
+    @Override
+    public boolean isReadOnly() {
+        return model.asMultiSelect().isReadOnly();
+    }
+
+    @Override
+    public void updateSelection(Set<T> addedItems, Set<T> removedItems) {
+        model.asMultiSelect().updateSelection(addedItems, removedItems);
+    }
+
+    @Override
+    public Set<T> getSelectedItems() {
+        return model.asMultiSelect().getSelectedItems();
+    }
+
+    @Override
+    public Registration addSelectionListener(
+            MultiSelectionListener<T> listener) {
+        return model.asMultiSelect().addSelectionListener(listener);
+    }
+}
diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridSingleSelect.java b/server/src/main/java/com/vaadin/ui/components/grid/GridSingleSelect.java
new file mode 100644 (file)
index 0000000..41804ad
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2000-2018 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.components.grid;
+
+import java.util.Optional;
+import java.util.Set;
+
+import com.vaadin.event.selection.SelectionListener;
+import com.vaadin.event.selection.SingleSelectionListener;
+import com.vaadin.shared.Registration;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.SingleSelect;
+
+/**
+ * Wrapper class to wrap Grid into a SingleSelect. This class also provides
+ * useful access to API of SingleSelectionModel.
+ *
+ * @param <T>
+ *            the bean type of grid
+ * @since
+ */
+public class GridSingleSelect<T> implements SingleSelect<T> {
+
+    private SingleSelectionModel<T> model;
+
+    /**
+     * Constructs a SingleSelect wrapper for given Grid.
+     *
+     * @param grid
+     *            the grid to wrap
+     */
+    public GridSingleSelect(Grid<T> grid) {
+        GridSelectionModel<T> selectionModel = grid.getSelectionModel();
+        if (!(selectionModel instanceof SingleSelectionModel)) {
+            throw new IllegalStateException(
+                    "Grid is not in singleiselect mode, it needs to be explicitly set to such with setSelectionModel(SingleSelectionModel) before being able to use singleselection features.");
+        }
+        model = (SingleSelectionModel<T>) selectionModel;
+    }
+
+    /* API for SingleSelectionModel */
+
+    /**
+     * 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) {
+        model.setDeselectAllowed(deselectAllowed);
+    }
+
+    /**
+     * Gets 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() {
+        return model.isDeselectAllowed();
+    }
+
+    /**
+     * Adds a single selection listener that is called when the value of this
+     * select is changed either by the user or programmatically.
+     *
+     * @param listener
+     *            the value change listener, not {@code null}
+     * @return a registration for the listener
+     */
+    public Registration addSingleSelectionListener(
+            SingleSelectionListener<T> listener) {
+        return model.addSingleSelectionListener(listener);
+    }
+
+    /**
+     * Returns the currently selected item, or an empty optional if no item is
+     * selected.
+     *
+     * @return an optional of the selected item if any, an empty optional
+     *         otherwise
+     */
+    public Optional<T> getSelectedItem() {
+        return model.getSelectedItem();
+    }
+
+    /**
+     * Sets the current selection to the given item, or clears selection if
+     * given {@code null}.
+     *
+     * @param item
+     *            the item to select or {@code null} to clear selection
+     */
+    public void setSelectedItem(T item) {
+        model.setSelectedItem(item);
+    }
+
+    /* Generic SelectionModel API */
+
+    /**
+     * Sets whether the user is allowed to change the selection.
+     * <p>
+     * The check is done only for the client side actions. It doesn't affect
+     * selection requests sent from the server side.
+     *
+     * @param allowed
+     *            <code>true</code> if the user is allowed to change the
+     *            selection, <code>false</code> otherwise
+     */
+    public void setUserSelectionAllowed(boolean allowed) {
+        model.setUserSelectionAllowed(allowed);
+    }
+
+    /**
+     * Checks if the user is allowed to change the selection.
+     * <p>
+     * The check is done only for the client side actions. It doesn't affect
+     * selection requests sent from the server side.
+     *
+     * @return <code>true</code> if the user is allowed to change the selection,
+     *         <code>false</code> otherwise
+     */
+    public boolean isUserSelectionAllowed() {
+        return model.isUserSelectionAllowed();
+    }
+
+    /**
+     * Returns a singleton set of the currently selected item or an empty set if
+     * no item is selected.
+     *
+     * @return a singleton set of the selected item if any, an empty set
+     *         otherwise
+     *
+     * @see #getSelectedItem()
+     */
+    public Set<T> getSelectedItems() {
+        return model.getSelectedItems();
+    }
+
+    /**
+     * Get first selected data item.
+     *
+     * @return the first selected item.
+     */
+    public Optional<T> getFirstSelectedItem() {
+        return model.getFirstSelectedItem();
+    }
+
+    /**
+     * Selects the given item. Depending on the implementation, may cause other
+     * items to be deselected. If the item is already selected, does nothing.
+     *
+     * @param item
+     *            the item to select, not null
+     */
+    public void deselect(T item) {
+        model.deselect(item);
+    }
+
+    /**
+     * Deselects all currently selected items, if any.
+     */
+    public void deselectAll() {
+        model.deselectAll();
+    }
+
+    /**
+     * Adds a generic listener to this selection model, accepting both single
+     * and multiselection events.
+     * <p>
+     * Use {@link #addSingleSelectionListener(SingleSelectionListener)} for more
+     * specific single selection event.
+     *
+     * @see #addSingleSelectionListener(SingleSelectionListener)
+     *
+     * @param listener
+     *            the listener to add
+     * @return a registration handle for removing the listener
+     */
+    public Registration addSelectionListener(SelectionListener<T> listener) {
+        return model.addSelectionListener(listener);
+    }
+
+    /**
+     * Returns whether the given item is currently selected.
+     *
+     * @param item
+     *            the item to check, not null
+     * @return {@code true} if the item is selected, {@code false} otherwise
+     */
+    public boolean isSelected(T item) {
+        return model.isSelected(item);
+    }
+
+    /**
+     * Selects the given item. If another item was already selected, that item
+     * is deselected.
+     * 
+     * @param item
+     *            the item to select
+     */
+    public void select(T item) {
+        model.select(item);
+    }
+
+    /* SingleSelect implementation */
+
+    @Override
+    public void setValue(T value) {
+        model.asSingleSelect().setValue(value);
+    }
+
+    @Override
+    public T getValue() {
+        return model.asSingleSelect().getValue();
+    }
+
+    @Override
+    public Registration addValueChangeListener(
+            ValueChangeListener<T> listener) {
+        return model.asSingleSelect().addValueChangeListener(listener);
+    }
+
+    @Override
+    public void setRequiredIndicatorVisible(boolean requiredIndicatorVisible) {
+        model.asSingleSelect()
+                .setRequiredIndicatorVisible(requiredIndicatorVisible);
+    }
+
+    @Override
+    public boolean isRequiredIndicatorVisible() {
+        return model.asSingleSelect().isRequiredIndicatorVisible();
+    }
+
+    @Override
+    public void setReadOnly(boolean readOnly) {
+        model.asSingleSelect().setReadOnly(readOnly);
+    }
+
+    @Override
+    public boolean isReadOnly() {
+        return model.asSingleSelect().isReadOnly();
+    }
+}
index c8ca4cf791a25634bce8eaa71ec21947d9af80f9..15413eb2d7970504f4b108b11dca431d9da440a5 100644 (file)
@@ -2,13 +2,26 @@ package com.vaadin.tests.components.grid;
 
 import static org.junit.Assert.assertEquals;
 
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.vaadin.data.SelectionModel;
+import com.vaadin.server.Extension;
 import com.vaadin.ui.Grid;
 import com.vaadin.ui.Grid.SelectionMode;
+import com.vaadin.ui.components.grid.GridMultiSelect;
+import com.vaadin.ui.components.grid.GridSingleSelect;
+import com.vaadin.ui.components.grid.MultiSelectionModel;
 import com.vaadin.ui.components.grid.MultiSelectionModelImpl;
 import com.vaadin.ui.components.grid.NoSelectionModel;
+import com.vaadin.ui.components.grid.SingleSelectionModel;
 import com.vaadin.ui.components.grid.SingleSelectionModelImpl;
 
 public class GridSelectionModeTest {
@@ -47,4 +60,53 @@ public class GridSelectionModeTest {
         grid.setSelectionMode(null);
     }
 
+    @Test
+    public void testGridAsMultiSelectHasAllAPI() {
+        assertAllAPIAvailable(GridMultiSelect.class, MultiSelectionModel.class,
+                "asMultiSelect");
+    }
+
+    @Test
+    public void testGridAsSingleSelectHasAllAPI() {
+        assertAllAPIAvailable(GridSingleSelect.class,
+                SingleSelectionModel.class, "asSingleSelect");
+    }
+
+    @SuppressWarnings("rawtypes")
+    protected void assertAllAPIAvailable(Class<?> testedClass,
+            Class<? extends SelectionModel> selectionModelClass,
+            String... ignoredMethods) {
+        List<String> ignored = Arrays.asList(ignoredMethods);
+        List<Method> missing = new ArrayList<>();
+        Arrays.stream(selectionModelClass.getMethods()).filter(method -> {
+            if (ignored.contains(method.getName())) {
+                // Explicitly ignored method.
+                return false;
+            }
+
+            try {
+                // Skip methods from Extension interface
+                Extension.class.getMethod(method.getName(),
+                        method.getParameterTypes());
+                return false;
+            } catch (Exception e) {
+                return true;
+            }
+        }).forEach(method ->
+
+        {
+            try {
+                testedClass.getMethod(method.getName(),
+                        method.getParameterTypes());
+            } catch (Exception e) {
+                missing.add(method);
+            }
+        });
+        if (!missing.isEmpty()) {
+            Assert.fail("Methods "
+                    + missing.stream().map(Method::getName)
+                            .collect(Collectors.joining(", "))
+                    + " not found in " + testedClass.getSimpleName());
+        }
+    }
 }