]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add simple data change handling for Selects
authorTeemu Suo-Anttila <teemusa@vaadin.com>
Wed, 31 Aug 2016 13:03:20 +0000 (16:03 +0300)
committerVaadin Code Review <review@vaadin.com>
Thu, 1 Sep 2016 08:50:59 +0000 (08:50 +0000)
Change-Id: I16f9577ea4091fb4febe167d76e141b5945f53ab

25 files changed:
client/src/main/java/com/vaadin/client/data/AbstractRemoteDataSource.java
client/src/main/java/com/vaadin/client/data/CacheStrategy.java
client/src/main/java/com/vaadin/client/data/DataSource.java
client/src/main/java/com/vaadin/client/data/SimpleDataChangeHandler.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/widget/escalator/RowVisibilityChangeEvent.java
client/src/main/java/com/vaadin/client/widget/grid/DataAvailableEvent.java
client/src/main/java/com/vaadin/client/widgets/Escalator.java
client/src/main/java/com/vaadin/client/widgets/Grid.java
client/src/test/java/com/vaadin/client/ui/grid/PartitioningTest.java
compatibility-client/src/main/java/com/vaadin/v7/client/connectors/MultiSelectionModelConnector.java
compatibility-client/src/main/java/com/vaadin/v7/client/connectors/RpcDataSourceConnector.java
compatibility-client/src/main/java/com/vaadin/v7/client/widget/escalator/RowVisibilityChangeEvent.java
compatibility-client/src/main/java/com/vaadin/v7/client/widget/grid/DataAvailableEvent.java
compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Escalator.java
compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java
compatibility-server/src/main/java/com/vaadin/v7/server/communication/data/RpcDataProviderExtension.java
server/src/main/java/com/vaadin/server/data/DataCommunicator.java
shared/src/main/java/com/vaadin/shared/Range.java [new file with mode: 0644]
shared/src/main/java/com/vaadin/shared/ui/grid/Range.java [deleted file]
shared/src/test/java/com/vaadin/shared/ui/grid/RangeTest.java
uitest/src/main/java/com/vaadin/tests/data/DummyData.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/widgetset/client/data/DummyComponentConnector.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java
uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/escalator/EscalatorSpacerTest.java
uitest/src/test/java/com/vaadin/tests/data/DummyDataTest.java [new file with mode: 0644]

index 95e013ed282a29fef3c652087ca69c56764cf5b6..71568cac7acbbecf484d0b61e92b2fcdbad9e807 100644 (file)
@@ -28,8 +28,8 @@ import com.google.gwt.core.client.Duration;
 import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.vaadin.client.Profiler;
+import com.vaadin.shared.Range;
 import com.vaadin.shared.Registration;
-import com.vaadin.shared.ui.grid.Range;
 
 /**
  * Base implementation for data sources that fetch data from a remote system.
index 9410223ab21830b66da0f8a52a6fea0761b37735..acfadcbcf796ecf3b2e4819e23115a61923c8f58 100644 (file)
@@ -16,7 +16,7 @@
 
 package com.vaadin.client.data;
 
-import com.vaadin.shared.ui.grid.Range;
+import com.vaadin.shared.Range;
 
 /**
  * Determines what data an {@link AbstractRemoteDataSource} should fetch and
index 3457b7b9fc5bc38ed87094f22654584b078d915d..fdec4b9a38e6122a8b75703f1e34ead0d2dd2368 100644 (file)
@@ -16,6 +16,9 @@
 
 package com.vaadin.client.data;
 
+import java.util.function.Consumer;
+
+import com.vaadin.shared.Range;
 import com.vaadin.shared.Registration;
 
 /**
@@ -104,7 +107,7 @@ public interface DataSource<T> {
          * override of an existing method, we're defining a new method for that
          * instead.
          *
-         * @param rowHandle
+         * @param obj
          *            the reference object with which to compare
          * @return {@code true} if this object is the same as the obj argument;
          *         {@code false} otherwise.
@@ -182,10 +185,27 @@ public interface DataSource<T> {
      *
      * @param dataChangeHandler
      *            the data change handler
+     *
+     * @return registration for removing the handler
      */
     public Registration addDataChangeHandler(
             DataChangeHandler dataChangeHandler);
 
+    /**
+     * Sets a simple data change handler for a widget without lazy loading.
+     * Refresh method should reset all the data in the widget.
+     *
+     * @param refreshMethod
+     *            a method to refresh all data in the widget
+     *
+     * @return registration for removing the handler
+     */
+    public default Registration addDataChangeHandler(
+            Consumer<Range> refreshMethod) {
+        return addDataChangeHandler(
+                new SimpleDataChangeHandler(this, refreshMethod));
+    }
+
     /**
      * Gets a {@link RowHandle} of a row object in the cache.
      *
diff --git a/client/src/main/java/com/vaadin/client/data/SimpleDataChangeHandler.java b/client/src/main/java/com/vaadin/client/data/SimpleDataChangeHandler.java
new file mode 100644 (file)
index 0000000..46fb019
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.client.data;
+
+import java.util.function.Consumer;
+
+import com.google.gwt.core.client.Scheduler;
+import com.vaadin.shared.Range;
+
+/**
+ * Helper class for creating a {@link DataChangeHandler} for a Widget that does
+ * not support lazy loading.
+ *
+ * @author Vaadin Ltd
+ * @since
+ */
+public class SimpleDataChangeHandler implements DataChangeHandler {
+
+    /**
+     * Class to request the data source to get the full data set.
+     */
+    private static class DelayedResetScheduler {
+
+        private final DataSource<?> dataSource;
+        private boolean scheduled = false;
+
+        public DelayedResetScheduler(DataSource<?> dataSource) {
+            this.dataSource = dataSource;
+        }
+
+        public void schedule() {
+            if (scheduled) {
+                return;
+            }
+            Scheduler.get().scheduleFinally(() -> {
+                dataSource.ensureAvailability(0, dataSource.size());
+                scheduled = false;
+            });
+            scheduled = true;
+        }
+
+        public int getExpectedSize() {
+            return dataSource.size();
+        }
+
+        public boolean isScheduled() {
+            return scheduled;
+        }
+    }
+
+    private final DelayedResetScheduler scheduler;
+    private final Consumer<Range> refreshMethod;
+
+    SimpleDataChangeHandler(DataSource<?> dataSource,
+            Consumer<Range> refreshMethod) {
+        scheduler = new DelayedResetScheduler(dataSource);
+        this.refreshMethod = refreshMethod;
+    }
+
+    @Override
+    public void dataUpdated(int firstRowIndex, int numberOfRows) {
+        scheduler.schedule();
+    }
+
+    @Override
+    public void dataRemoved(int firstRowIndex, int numberOfRows) {
+        scheduler.schedule();
+    }
+
+    @Override
+    public void dataAdded(int firstRowIndex, int numberOfRows) {
+        scheduler.schedule();
+    }
+
+    @Override
+    public void dataAvailable(int firstRowIndex, int numberOfRows) {
+        if (!scheduler.isScheduled() && firstRowIndex == 0
+                && numberOfRows == scheduler.getExpectedSize()) {
+            // All data should now be available.
+            refreshMethod.accept(Range.withLength(firstRowIndex, numberOfRows));
+        } else {
+            scheduler.schedule();
+        }
+    }
+
+    @Override
+    public void resetDataAndSize(int newSize) {
+        scheduler.schedule();
+    }
+}
index ce4be72b6857c7d9ee8eb3b5edd846696297e2cc..a36b591fd076d17606ec34d7b449f0d283108b1a 100644 (file)
@@ -17,7 +17,7 @@
 package com.vaadin.client.widget.escalator;
 
 import com.google.gwt.event.shared.GwtEvent;
-import com.vaadin.shared.ui.grid.Range;
+import com.vaadin.shared.Range;
 
 /**
  * Event fired when the range of visible rows changes e.g. because of scrolling.
index f4ecf1167759e34d845cd23225100c79242be186..8441a16bc52e15ac69d272eb30986730d6e92077 100644 (file)
@@ -16,7 +16,7 @@
 package com.vaadin.client.widget.grid;
 
 import com.google.gwt.event.shared.GwtEvent;
-import com.vaadin.shared.ui.grid.Range;
+import com.vaadin.shared.Range;
 
 /**
  * Event object describing a change of row availability in DataSource of a Grid.
index a5e95a26a5d80bcd6ec1199b505b0b081991b888..35978b7cb0a413e6091ae05235896e2fe5efbb8c 100644 (file)
@@ -89,8 +89,8 @@ import com.vaadin.client.widget.escalator.events.RowHeightChangedEvent;
 import com.vaadin.client.widget.grid.events.ScrollEvent;
 import com.vaadin.client.widget.grid.events.ScrollHandler;
 import com.vaadin.client.widgets.Escalator.JsniUtil.TouchHandlerBundle;
+import com.vaadin.shared.Range;
 import com.vaadin.shared.ui.grid.HeightMode;
-import com.vaadin.shared.ui.grid.Range;
 import com.vaadin.shared.ui.grid.ScrollDestination;
 import com.vaadin.shared.util.SharedUtil;
 
index 990c85dd1c1e59725526f1a52dddf35835e9b77c..85c6d489428b881452b75a0e619f9d11cc2aaa0d 100644 (file)
@@ -174,13 +174,13 @@ import com.vaadin.client.widgets.Escalator.SubPartArguments;
 import com.vaadin.client.widgets.Grid.Editor.State;
 import com.vaadin.client.widgets.Grid.StaticSection.StaticCell;
 import com.vaadin.client.widgets.Grid.StaticSection.StaticRow;
+import com.vaadin.shared.Range;
 import com.vaadin.shared.Registration;
 import com.vaadin.shared.data.sort.SortDirection;
 import com.vaadin.shared.ui.grid.GridConstants;
 import com.vaadin.shared.ui.grid.GridConstants.Section;
 import com.vaadin.shared.ui.grid.GridStaticCellType;
 import com.vaadin.shared.ui.grid.HeightMode;
-import com.vaadin.shared.ui.grid.Range;
 import com.vaadin.shared.ui.grid.ScrollDestination;
 import com.vaadin.shared.util.SharedUtil;
 
index ac4d39006abbeb0d70d50f28f70a8e8d729bd586..8be729e7c48126370afaeca71ac3316658eec3f8 100644 (file)
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
 
-import com.vaadin.shared.ui.grid.Range;
+import com.vaadin.shared.Range;
 
 @SuppressWarnings("static-method")
 public class PartitioningTest {
index bdaf0f1f88fcc7abcaa01fa0b603e86c9ed79cb3..5103abb2852c3c524906092ac8147a3e018aa323 100644 (file)
@@ -29,8 +29,8 @@ import com.vaadin.client.ServerConnector;
 import com.vaadin.client.annotations.OnStateChange;
 import com.vaadin.client.data.DataSource;
 import com.vaadin.client.data.DataSource.RowHandle;
+import com.vaadin.shared.Range;
 import com.vaadin.shared.ui.Connect;
-import com.vaadin.shared.ui.grid.Range;
 import com.vaadin.v7.client.renderers.ComplexRenderer;
 import com.vaadin.v7.client.renderers.Renderer;
 import com.vaadin.v7.client.widget.grid.DataAvailableEvent;
index 439305d71899023f419616384fdfeeb61de3efaf..363d3c047da430b163bd643b038d6ed7b5d0c784 100644 (file)
@@ -23,10 +23,10 @@ import java.util.List;
 import com.vaadin.client.ServerConnector;
 import com.vaadin.client.data.AbstractRemoteDataSource;
 import com.vaadin.client.extensions.AbstractExtensionConnector;
+import com.vaadin.shared.Range;
 import com.vaadin.shared.data.DataProviderRpc;
 import com.vaadin.shared.data.DataRequestRpc;
 import com.vaadin.shared.ui.Connect;
-import com.vaadin.shared.ui.grid.Range;
 import com.vaadin.v7.shared.ui.grid.GridState;
 
 import elemental.json.Json;
index d5c00015948aa2fc67b27dbb349e22a0fceebc72..ad2b6c65a6a07abb059e7b698caef341d9820a4d 100644 (file)
@@ -17,7 +17,7 @@
 package com.vaadin.v7.client.widget.escalator;
 
 import com.google.gwt.event.shared.GwtEvent;
-import com.vaadin.shared.ui.grid.Range;
+import com.vaadin.shared.Range;
 
 /**
  * Event fired when the range of visible rows changes e.g. because of scrolling.
index 37b171894d766faaf0b4178b527aa4ae158b31b2..9f51c73047997214dd08bbbd294fa5ef8b4b4686 100644 (file)
@@ -16,7 +16,7 @@
 package com.vaadin.v7.client.widget.grid;
 
 import com.google.gwt.event.shared.GwtEvent;
-import com.vaadin.shared.ui.grid.Range;
+import com.vaadin.shared.Range;
 
 /**
  * Event object describing a change of row availability in DataSource of a Grid.
index f8f484850812fbd67f537da3560602ead83146a3..0804a0ad5e9fc0e0ea234065f22d1a1a0e700d08 100644 (file)
@@ -65,7 +65,7 @@ import com.vaadin.client.DeferredWorker;
 import com.vaadin.client.Profiler;
 import com.vaadin.client.WidgetUtil;
 import com.vaadin.client.ui.SubPartAware;
-import com.vaadin.shared.ui.grid.Range;
+import com.vaadin.shared.Range;
 import com.vaadin.shared.util.SharedUtil;
 import com.vaadin.v7.client.widget.escalator.Cell;
 import com.vaadin.v7.client.widget.escalator.ColumnConfiguration;
index 9069c95e41b179369a4bdd63115b446b7389c41d..e79237702b7cc1880a067e01b1d5e445dd0c8fbb 100644 (file)
@@ -90,9 +90,9 @@ import com.vaadin.client.ui.dd.DragAndDropHandler.DragAndDropCallback;
 import com.vaadin.client.ui.dd.DragHandle;
 import com.vaadin.client.ui.dd.DragHandle.DragHandleCallback;
 import com.vaadin.client.widgets.Overlay;
+import com.vaadin.shared.Range;
 import com.vaadin.shared.Registration;
 import com.vaadin.shared.data.sort.SortDirection;
-import com.vaadin.shared.ui.grid.Range;
 import com.vaadin.shared.util.SharedUtil;
 import com.vaadin.v7.client.renderers.ComplexRenderer;
 import com.vaadin.v7.client.renderers.Renderer;
index ce960fa5fd06beae6bc729963c736eb019948727..7fdcd4eff4f817f703fc68846203fb83890c70c1 100644 (file)
@@ -29,9 +29,9 @@ import java.util.Set;
 import com.vaadin.server.AbstractExtension;
 import com.vaadin.server.ClientConnector;
 import com.vaadin.server.KeyMapper;
+import com.vaadin.shared.Range;
 import com.vaadin.shared.data.DataProviderRpc;
 import com.vaadin.shared.data.DataRequestRpc;
-import com.vaadin.shared.ui.grid.Range;
 import com.vaadin.v7.data.Container;
 import com.vaadin.v7.data.Container.Indexed;
 import com.vaadin.v7.data.Container.Indexed.ItemAddEvent;
index 7bf9707741b0c351555d05e589d3dd8450fd9120..8c92ad50e316dd58abad949b92d94bd42514852b 100644 (file)
@@ -31,10 +31,10 @@ import java.util.stream.Stream;
 
 import com.vaadin.server.AbstractExtension;
 import com.vaadin.server.KeyMapper;
+import com.vaadin.shared.Range;
 import com.vaadin.shared.data.DataCommunicatorClientRpc;
 import com.vaadin.shared.data.DataCommunicatorConstants;
 import com.vaadin.shared.data.DataRequestRpc;
-import com.vaadin.shared.ui.grid.Range;
 
 import elemental.json.Json;
 import elemental.json.JsonArray;
diff --git a/shared/src/main/java/com/vaadin/shared/Range.java b/shared/src/main/java/com/vaadin/shared/Range.java
new file mode 100644 (file)
index 0000000..98d9ba5
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * 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.shared;
+
+import java.io.Serializable;
+
+/**
+ * An immutable representation of a range, marked by start and end points.
+ * <p>
+ * The range is treated as inclusive at the start, and exclusive at the end.
+ * I.e. the range [0..1[ has the length 1, and represents one integer: 0.
+ * <p>
+ * The range is considered {@link #isEmpty() empty} if the start is the same as
+ * the end.
+ *
+ * @since 7.4
+ * @author Vaadin Ltd
+ */
+public final class Range implements Serializable {
+    private final int start;
+    private final int end;
+
+    /**
+     * Creates a range object representing a single integer.
+     *
+     * @param integer
+     *            the number to represent as a range
+     * @return the range represented by <code>integer</code>
+     */
+    public static Range withOnly(final int integer) {
+        return new Range(integer, integer + 1);
+    }
+
+    /**
+     * Creates a range between two integers.
+     * <p>
+     * The range start is <em>inclusive</em> and the end is <em>exclusive</em>.
+     * So, a range "between" 0 and 5 represents the numbers 0, 1, 2, 3 and 4,
+     * but not 5.
+     *
+     * @param start
+     *            the start of the the range, inclusive
+     * @param end
+     *            the end of the range, exclusive
+     * @return a range representing <code>[start..end[</code>
+     * @throws IllegalArgumentException
+     *             if <code>start &gt; end</code>
+     */
+    public static Range between(final int start, final int end)
+            throws IllegalArgumentException {
+        return new Range(start, end);
+    }
+
+    /**
+     * Creates a range from a start point, with a given length.
+     *
+     * @param start
+     *            the first integer to include in the range
+     * @param length
+     *            the length of the resulting range
+     * @return a range starting from <code>start</code>, with
+     *         <code>length</code> number of integers following
+     * @throws IllegalArgumentException
+     *             if length &lt; 0
+     */
+    public static Range withLength(final int start, final int length)
+            throws IllegalArgumentException {
+        if (length < 0) {
+            /*
+             * The constructor of Range will throw an exception if start >
+             * start+length (i.e. if length is negative). We're throwing the
+             * same exception type, just with a more descriptive message.
+             */
+            throw new IllegalArgumentException("length must not be negative");
+        }
+        return new Range(start, start + length);
+    }
+
+    /**
+     * Creates a new range between two numbers: <code>[start..end[</code>.
+     *
+     * @param start
+     *            the start integer, inclusive
+     * @param end
+     *            the end integer, exclusive
+     * @throws IllegalArgumentException
+     *             if <code>start &gt; end</code>
+     */
+    private Range(final int start, final int end)
+            throws IllegalArgumentException {
+        if (start > end) {
+            throw new IllegalArgumentException(
+                    "start must not be greater than end");
+        }
+
+        this.start = start;
+        this.end = end;
+    }
+
+    /**
+     * Returns the <em>inclusive</em> start point of this range.
+     *
+     * @return the start point of this range
+     */
+    public int getStart() {
+        return start;
+    }
+
+    /**
+     * Returns the <em>exclusive</em> end point of this range.
+     *
+     * @return the end point of this range
+     */
+    public int getEnd() {
+        return end;
+    }
+
+    /**
+     * The number of integers contained in the range.
+     *
+     * @return the number of integers contained in the range
+     */
+    public int length() {
+        return getEnd() - getStart();
+    }
+
+    /**
+     * Checks whether the range has no elements between the start and end.
+     *
+     * @return <code>true</code> iff the range contains no elements.
+     */
+    public boolean isEmpty() {
+        return getStart() >= getEnd();
+    }
+
+    /**
+     * Checks whether this range and another range are at least partially
+     * covering the same values.
+     *
+     * @param other
+     *            the other range to check against
+     * @return <code>true</code> if this and <code>other</code> intersect
+     */
+    public boolean intersects(final Range other) {
+        return getStart() < other.getEnd() && other.getStart() < getEnd();
+    }
+
+    /**
+     * Checks whether an integer is found within this range.
+     *
+     * @param integer
+     *            an integer to test for presence in this range
+     * @return <code>true</code> iff <code>integer</code> is in this range
+     */
+    public boolean contains(final int integer) {
+        return getStart() <= integer && integer < getEnd();
+    }
+
+    /**
+     * Checks whether this range is a subset of another range.
+     *
+     * @return <code>true</code> iff <code>other</code> completely wraps this
+     *         range
+     */
+    public boolean isSubsetOf(final Range other) {
+        if (isEmpty() && other.isEmpty()) {
+            return true;
+        }
+
+        return other.getStart() <= getStart() && getEnd() <= other.getEnd();
+    }
+
+    /**
+     * Overlay this range with another one, and partition the ranges according
+     * to how they position relative to each other.
+     * <p>
+     * The three partitions are returned as a three-element Range array:
+     * <ul>
+     * <li>Elements in this range that occur before elements in
+     * <code>other</code>.
+     * <li>Elements that are shared between the two ranges.
+     * <li>Elements in this range that occur after elements in
+     * <code>other</code>.
+     * </ul>
+     *
+     * @param other
+     *            the other range to act as delimiters.
+     * @return a three-element Range array of partitions depicting the elements
+     *         before (index 0), shared/inside (index 1) and after (index 2).
+     */
+    public Range[] partitionWith(final Range other) {
+        final Range[] splitBefore = splitAt(other.getStart());
+        final Range rangeBefore = splitBefore[0];
+        final Range[] splitAfter = splitBefore[1].splitAt(other.getEnd());
+        final Range rangeInside = splitAfter[0];
+        final Range rangeAfter = splitAfter[1];
+        return new Range[] { rangeBefore, rangeInside, rangeAfter };
+    }
+
+    /**
+     * Get a range that is based on this one, but offset by a number.
+     *
+     * @param offset
+     *            the number to offset by
+     * @return a copy of this range, offset by <code>offset</code>
+     */
+    public Range offsetBy(final int offset) {
+        if (offset == 0) {
+            return this;
+        } else {
+            return new Range(start + offset, end + offset);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " [" + getStart() + ".." + getEnd()
+                + "[" + (isEmpty() ? " (empty)" : "");
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + end;
+        result = prime * result + start;
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Range other = (Range) obj;
+        if (end != other.end) {
+            return false;
+        }
+        if (start != other.start) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Checks whether this range starts before the start of another range.
+     *
+     * @param other
+     *            the other range to compare against
+     * @return <code>true</code> iff this range starts before the
+     *         <code>other</code>
+     */
+    public boolean startsBefore(final Range other) {
+        return getStart() < other.getStart();
+    }
+
+    /**
+     * Checks whether this range ends before the start of another range.
+     *
+     * @param other
+     *            the other range to compare against
+     * @return <code>true</code> iff this range ends before the
+     *         <code>other</code>
+     */
+    public boolean endsBefore(final Range other) {
+        return getEnd() <= other.getStart();
+    }
+
+    /**
+     * Checks whether this range ends after the end of another range.
+     *
+     * @param other
+     *            the other range to compare against
+     * @return <code>true</code> iff this range ends after the
+     *         <code>other</code>
+     */
+    public boolean endsAfter(final Range other) {
+        return getEnd() > other.getEnd();
+    }
+
+    /**
+     * Checks whether this range starts after the end of another range.
+     *
+     * @param other
+     *            the other range to compare against
+     * @return <code>true</code> iff this range starts after the
+     *         <code>other</code>
+     */
+    public boolean startsAfter(final Range other) {
+        return getStart() >= other.getEnd();
+    }
+
+    /**
+     * Split the range into two at a certain integer.
+     * <p>
+     * <em>Example:</em> <code>[5..10[.splitAt(7) == [5..7[, [7..10[</code>
+     *
+     * @param integer
+     *            the integer at which to split the range into two
+     * @return an array of two ranges, with <code>[start..integer[</code> in the
+     *         first element, and <code>[integer..end[</code> in the second
+     *         element.
+     *         <p>
+     *         If {@code integer} is less than {@code start}, [empty,
+     *         {@code this} ] is returned. if <code>integer</code> is equal to
+     *         or greater than {@code end}, [{@code this}, empty] is returned
+     *         instead.
+     */
+    public Range[] splitAt(final int integer) {
+        if (integer < start) {
+            return new Range[] { Range.withLength(start, 0), this };
+        } else if (integer >= end) {
+            return new Range[] { this, Range.withLength(end, 0) };
+        } else {
+            return new Range[] { new Range(start, integer),
+                    new Range(integer, end) };
+        }
+    }
+
+    /**
+     * Split the range into two after a certain number of integers into the
+     * range.
+     * <p>
+     * Calling this method is equivalent to calling
+     * <code>{@link #splitAt(int) splitAt}({@link #getStart()}+length);</code>
+     * <p>
+     * <em>Example:</em>
+     * <code>[5..10[.splitAtFromStart(2) == [5..7[, [7..10[</code>
+     *
+     * @param length
+     *            the length at which to split this range into two
+     * @return an array of two ranges, having the <code>length</code>-first
+     *         elements of this range, and the second range having the rest. If
+     *         <code>length</code> &leq; 0, the first element will be empty, and
+     *         the second element will be this range. If <code>length</code>
+     *         &geq; {@link #length()}, the first element will be this range,
+     *         and the second element will be empty.
+     */
+    public Range[] splitAtFromStart(final int length) {
+        return splitAt(getStart() + length);
+    }
+
+    /**
+     * Combines two ranges to create a range containing all values in both
+     * ranges, provided there are no gaps between the ranges.
+     *
+     * @param other
+     *            the range to combine with this range
+     *
+     * @return the combined range
+     *
+     * @throws IllegalArgumentException
+     *             if the two ranges aren't connected
+     */
+    public Range combineWith(Range other) throws IllegalArgumentException {
+        if (getStart() > other.getEnd() || other.getStart() > getEnd()) {
+            throw new IllegalArgumentException(
+                    "There is a gap between " + this + " and " + other);
+        }
+
+        return Range.between(Math.min(getStart(), other.getStart()),
+                Math.max(getEnd(), other.getEnd()));
+    }
+
+    /**
+     * Creates a range that is expanded the given amounts in both ends.
+     *
+     * @param startDelta
+     *            the amount to expand by in the beginning of the range
+     * @param endDelta
+     *            the amount to expand by in the end of the range
+     *
+     * @return an expanded range
+     *
+     * @throws IllegalArgumentException
+     *             if the new range would have <code>start &gt; end</code>
+     */
+    public Range expand(int startDelta, int endDelta)
+            throws IllegalArgumentException {
+        return Range.between(getStart() - startDelta, getEnd() + endDelta);
+    }
+
+    /**
+     * Limits this range to be within the bounds of the provided range.
+     * <p>
+     * This is basically an optimized way of calculating
+     * <code>{@link #partitionWith(Range)}[1]</code> without the overhead of
+     * defining the parts that do not overlap.
+     * <p>
+     * If the two ranges do not intersect, an empty range is returned. There are
+     * no guarantees about the position of that range.
+     *
+     * @param bounds
+     *            the bounds that the returned range should be limited to
+     * @return a bounded range
+     */
+    public Range restrictTo(Range bounds) {
+        boolean startWithin = bounds.contains(getStart());
+        boolean endWithin = bounds.contains(getEnd());
+        boolean boundsWithin = getStart() < bounds.getStart()
+                && getEnd() >= bounds.getEnd();
+
+        if (startWithin) {
+            if (endWithin) {
+                return this;
+            } else {
+                return Range.between(getStart(), bounds.getEnd());
+            }
+        } else {
+            if (endWithin) {
+                return Range.between(bounds.getStart(), getEnd());
+            } else if (boundsWithin) {
+                return bounds;
+            } else {
+                return Range.withLength(getStart(), 0);
+            }
+        }
+    }
+}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/Range.java b/shared/src/main/java/com/vaadin/shared/ui/grid/Range.java
deleted file mode 100644 (file)
index 93805e4..0000000
+++ /dev/null
@@ -1,439 +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.shared.ui.grid;
-
-import java.io.Serializable;
-
-/**
- * An immutable representation of a range, marked by start and end points.
- * <p>
- * The range is treated as inclusive at the start, and exclusive at the end.
- * I.e. the range [0..1[ has the length 1, and represents one integer: 0.
- * <p>
- * The range is considered {@link #isEmpty() empty} if the start is the same as
- * the end.
- *
- * @since 7.4
- * @author Vaadin Ltd
- */
-public final class Range implements Serializable {
-    private final int start;
-    private final int end;
-
-    /**
-     * Creates a range object representing a single integer.
-     *
-     * @param integer
-     *            the number to represent as a range
-     * @return the range represented by <code>integer</code>
-     */
-    public static Range withOnly(final int integer) {
-        return new Range(integer, integer + 1);
-    }
-
-    /**
-     * Creates a range between two integers.
-     * <p>
-     * The range start is <em>inclusive</em> and the end is <em>exclusive</em>.
-     * So, a range "between" 0 and 5 represents the numbers 0, 1, 2, 3 and 4,
-     * but not 5.
-     *
-     * @param start
-     *            the start of the the range, inclusive
-     * @param end
-     *            the end of the range, exclusive
-     * @return a range representing <code>[start..end[</code>
-     * @throws IllegalArgumentException
-     *             if <code>start &gt; end</code>
-     */
-    public static Range between(final int start, final int end)
-            throws IllegalArgumentException {
-        return new Range(start, end);
-    }
-
-    /**
-     * Creates a range from a start point, with a given length.
-     *
-     * @param start
-     *            the first integer to include in the range
-     * @param length
-     *            the length of the resulting range
-     * @return a range starting from <code>start</code>, with
-     *         <code>length</code> number of integers following
-     * @throws IllegalArgumentException
-     *             if length &lt; 0
-     */
-    public static Range withLength(final int start, final int length)
-            throws IllegalArgumentException {
-        if (length < 0) {
-            /*
-             * The constructor of Range will throw an exception if start >
-             * start+length (i.e. if length is negative). We're throwing the
-             * same exception type, just with a more descriptive message.
-             */
-            throw new IllegalArgumentException("length must not be negative");
-        }
-        return new Range(start, start + length);
-    }
-
-    /**
-     * Creates a new range between two numbers: <code>[start..end[</code>.
-     *
-     * @param start
-     *            the start integer, inclusive
-     * @param end
-     *            the end integer, exclusive
-     * @throws IllegalArgumentException
-     *             if <code>start &gt; end</code>
-     */
-    private Range(final int start, final int end)
-            throws IllegalArgumentException {
-        if (start > end) {
-            throw new IllegalArgumentException(
-                    "start must not be greater than end");
-        }
-
-        this.start = start;
-        this.end = end;
-    }
-
-    /**
-     * Returns the <em>inclusive</em> start point of this range.
-     *
-     * @return the start point of this range
-     */
-    public int getStart() {
-        return start;
-    }
-
-    /**
-     * Returns the <em>exclusive</em> end point of this range.
-     *
-     * @return the end point of this range
-     */
-    public int getEnd() {
-        return end;
-    }
-
-    /**
-     * The number of integers contained in the range.
-     *
-     * @return the number of integers contained in the range
-     */
-    public int length() {
-        return getEnd() - getStart();
-    }
-
-    /**
-     * Checks whether the range has no elements between the start and end.
-     *
-     * @return <code>true</code> iff the range contains no elements.
-     */
-    public boolean isEmpty() {
-        return getStart() >= getEnd();
-    }
-
-    /**
-     * Checks whether this range and another range are at least partially
-     * covering the same values.
-     *
-     * @param other
-     *            the other range to check against
-     * @return <code>true</code> if this and <code>other</code> intersect
-     */
-    public boolean intersects(final Range other) {
-        return getStart() < other.getEnd() && other.getStart() < getEnd();
-    }
-
-    /**
-     * Checks whether an integer is found within this range.
-     *
-     * @param integer
-     *            an integer to test for presence in this range
-     * @return <code>true</code> iff <code>integer</code> is in this range
-     */
-    public boolean contains(final int integer) {
-        return getStart() <= integer && integer < getEnd();
-    }
-
-    /**
-     * Checks whether this range is a subset of another range.
-     *
-     * @return <code>true</code> iff <code>other</code> completely wraps this
-     *         range
-     */
-    public boolean isSubsetOf(final Range other) {
-        if (isEmpty() && other.isEmpty()) {
-            return true;
-        }
-
-        return other.getStart() <= getStart() && getEnd() <= other.getEnd();
-    }
-
-    /**
-     * Overlay this range with another one, and partition the ranges according
-     * to how they position relative to each other.
-     * <p>
-     * The three partitions are returned as a three-element Range array:
-     * <ul>
-     * <li>Elements in this range that occur before elements in
-     * <code>other</code>.
-     * <li>Elements that are shared between the two ranges.
-     * <li>Elements in this range that occur after elements in
-     * <code>other</code>.
-     * </ul>
-     *
-     * @param other
-     *            the other range to act as delimiters.
-     * @return a three-element Range array of partitions depicting the elements
-     *         before (index 0), shared/inside (index 1) and after (index 2).
-     */
-    public Range[] partitionWith(final Range other) {
-        final Range[] splitBefore = splitAt(other.getStart());
-        final Range rangeBefore = splitBefore[0];
-        final Range[] splitAfter = splitBefore[1].splitAt(other.getEnd());
-        final Range rangeInside = splitAfter[0];
-        final Range rangeAfter = splitAfter[1];
-        return new Range[] { rangeBefore, rangeInside, rangeAfter };
-    }
-
-    /**
-     * Get a range that is based on this one, but offset by a number.
-     *
-     * @param offset
-     *            the number to offset by
-     * @return a copy of this range, offset by <code>offset</code>
-     */
-    public Range offsetBy(final int offset) {
-        if (offset == 0) {
-            return this;
-        } else {
-            return new Range(start + offset, end + offset);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + " [" + getStart() + ".." + getEnd()
-                + "[" + (isEmpty() ? " (empty)" : "");
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + end;
-        result = prime * result + start;
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final Range other = (Range) obj;
-        if (end != other.end) {
-            return false;
-        }
-        if (start != other.start) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Checks whether this range starts before the start of another range.
-     *
-     * @param other
-     *            the other range to compare against
-     * @return <code>true</code> iff this range starts before the
-     *         <code>other</code>
-     */
-    public boolean startsBefore(final Range other) {
-        return getStart() < other.getStart();
-    }
-
-    /**
-     * Checks whether this range ends before the start of another range.
-     *
-     * @param other
-     *            the other range to compare against
-     * @return <code>true</code> iff this range ends before the
-     *         <code>other</code>
-     */
-    public boolean endsBefore(final Range other) {
-        return getEnd() <= other.getStart();
-    }
-
-    /**
-     * Checks whether this range ends after the end of another range.
-     *
-     * @param other
-     *            the other range to compare against
-     * @return <code>true</code> iff this range ends after the
-     *         <code>other</code>
-     */
-    public boolean endsAfter(final Range other) {
-        return getEnd() > other.getEnd();
-    }
-
-    /**
-     * Checks whether this range starts after the end of another range.
-     *
-     * @param other
-     *            the other range to compare against
-     * @return <code>true</code> iff this range starts after the
-     *         <code>other</code>
-     */
-    public boolean startsAfter(final Range other) {
-        return getStart() >= other.getEnd();
-    }
-
-    /**
-     * Split the range into two at a certain integer.
-     * <p>
-     * <em>Example:</em> <code>[5..10[.splitAt(7) == [5..7[, [7..10[</code>
-     *
-     * @param integer
-     *            the integer at which to split the range into two
-     * @return an array of two ranges, with <code>[start..integer[</code> in the
-     *         first element, and <code>[integer..end[</code> in the second
-     *         element.
-     *         <p>
-     *         If {@code integer} is less than {@code start}, [empty,
-     *         {@code this} ] is returned. if <code>integer</code> is equal to
-     *         or greater than {@code end}, [{@code this}, empty] is returned
-     *         instead.
-     */
-    public Range[] splitAt(final int integer) {
-        if (integer < start) {
-            return new Range[] { Range.withLength(start, 0), this };
-        } else if (integer >= end) {
-            return new Range[] { this, Range.withLength(end, 0) };
-        } else {
-            return new Range[] { new Range(start, integer),
-                    new Range(integer, end) };
-        }
-    }
-
-    /**
-     * Split the range into two after a certain number of integers into the
-     * range.
-     * <p>
-     * Calling this method is equivalent to calling
-     * <code>{@link #splitAt(int) splitAt}({@link #getStart()}+length);</code>
-     * <p>
-     * <em>Example:</em>
-     * <code>[5..10[.splitAtFromStart(2) == [5..7[, [7..10[</code>
-     *
-     * @param length
-     *            the length at which to split this range into two
-     * @return an array of two ranges, having the <code>length</code>-first
-     *         elements of this range, and the second range having the rest. If
-     *         <code>length</code> &leq; 0, the first element will be empty, and
-     *         the second element will be this range. If <code>length</code>
-     *         &geq; {@link #length()}, the first element will be this range,
-     *         and the second element will be empty.
-     */
-    public Range[] splitAtFromStart(final int length) {
-        return splitAt(getStart() + length);
-    }
-
-    /**
-     * Combines two ranges to create a range containing all values in both
-     * ranges, provided there are no gaps between the ranges.
-     *
-     * @param other
-     *            the range to combine with this range
-     *
-     * @return the combined range
-     *
-     * @throws IllegalArgumentException
-     *             if the two ranges aren't connected
-     */
-    public Range combineWith(Range other) throws IllegalArgumentException {
-        if (getStart() > other.getEnd() || other.getStart() > getEnd()) {
-            throw new IllegalArgumentException(
-                    "There is a gap between " + this + " and " + other);
-        }
-
-        return Range.between(Math.min(getStart(), other.getStart()),
-                Math.max(getEnd(), other.getEnd()));
-    }
-
-    /**
-     * Creates a range that is expanded the given amounts in both ends.
-     *
-     * @param startDelta
-     *            the amount to expand by in the beginning of the range
-     * @param endDelta
-     *            the amount to expand by in the end of the range
-     *
-     * @return an expanded range
-     *
-     * @throws IllegalArgumentException
-     *             if the new range would have <code>start &gt; end</code>
-     */
-    public Range expand(int startDelta, int endDelta)
-            throws IllegalArgumentException {
-        return Range.between(getStart() - startDelta, getEnd() + endDelta);
-    }
-
-    /**
-     * Limits this range to be within the bounds of the provided range.
-     * <p>
-     * This is basically an optimized way of calculating
-     * <code>{@link #partitionWith(Range)}[1]</code> without the overhead of
-     * defining the parts that do not overlap.
-     * <p>
-     * If the two ranges do not intersect, an empty range is returned. There are
-     * no guarantees about the position of that range.
-     *
-     * @param bounds
-     *            the bounds that the returned range should be limited to
-     * @return a bounded range
-     */
-    public Range restrictTo(Range bounds) {
-        boolean startWithin = bounds.contains(getStart());
-        boolean endWithin = bounds.contains(getEnd());
-        boolean boundsWithin = getStart() < bounds.getStart()
-                && getEnd() >= bounds.getEnd();
-
-        if (startWithin) {
-            if (endWithin) {
-                return this;
-            } else {
-                return Range.between(getStart(), bounds.getEnd());
-            }
-        } else {
-            if (endWithin) {
-                return Range.between(bounds.getStart(), getEnd());
-            } else if (boundsWithin) {
-                return bounds;
-            } else {
-                return Range.withLength(getStart(), 0);
-            }
-        }
-    }
-}
index bde581022b8f8f8280eaa6430e331ba9fb8847e1..921edf145e60c9399b198024b4350d4b7a115092 100644 (file)
@@ -20,6 +20,8 @@ import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
 
+import com.vaadin.shared.Range;
+
 @SuppressWarnings("static-method")
 public class RangeTest {
 
diff --git a/uitest/src/main/java/com/vaadin/tests/data/DummyData.java b/uitest/src/main/java/com/vaadin/tests/data/DummyData.java
new file mode 100644 (file)
index 0000000..60b3af7
--- /dev/null
@@ -0,0 +1,117 @@
+package com.vaadin.tests.data;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import com.vaadin.annotations.Widgetset;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.data.DataCommunicator;
+import com.vaadin.server.data.ListDataSource;
+import com.vaadin.server.data.Query;
+import com.vaadin.shared.data.DataCommunicatorConstants;
+import com.vaadin.shared.data.selection.SelectionModel;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.tests.widgetset.TestingWidgetSet;
+import com.vaadin.ui.AbstractListing;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.HorizontalLayout;
+
+@Widgetset(TestingWidgetSet.NAME)
+public class DummyData extends AbstractTestUIWithLog {
+
+    /**
+     * DataSource that keeps track on how often the data is requested.
+     */
+    private class LoggingDataSource extends ListDataSource<String> {
+        private int count = 0;
+
+        private LoggingDataSource(Collection<String> collection) {
+            super(collection);
+        }
+
+        @Override
+        public Stream<String> apply(Query query) {
+            log("Backend request #" + (count++));
+            return super.apply(query);
+        }
+    }
+
+    /**
+     * Simplified server only selection model. Selection state passed in data,
+     * shown as bold text.
+     */
+    private static class DummySelectionModel implements SelectionModel<String> {
+        private String selected;
+        private DataCommunicator<String> communicator;
+
+        @Override
+        public Set<String> getSelectedItems() {
+            if (selected != null) {
+                return Collections.singleton(selected);
+            }
+            return Collections.emptySet();
+        }
+
+        @Override
+        public void select(String item) {
+            if (selected != null) {
+                communicator.refresh(selected);
+            }
+            selected = item;
+            if (selected != null) {
+                communicator.refresh(selected);
+            }
+        }
+
+        @Override
+        public void deselect(String item) {
+            if (item == selected) {
+                select(null);
+            }
+        }
+
+        private void setCommunicator(DataCommunicator<String> dataComm) {
+            communicator = dataComm;
+        }
+    }
+
+    public static class DummyComponent
+            extends AbstractListing<String, DummySelectionModel> {
+
+        private DummyComponent() {
+            setSelectionModel(new DummySelectionModel());
+            addDataGenerator((str, json) -> {
+                json.put(DataCommunicatorConstants.DATA, str);
+                if (isSelected(str)) {
+                    json.put(DataCommunicatorConstants.SELECTED, true);
+                }
+            });
+            getSelectionModel().setCommunicator(getDataCommunicator());
+        }
+    }
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        DummyComponent dummy = new DummyComponent();
+        List<String> items = new ArrayList<>();
+        for (int i = 0; i < 300; ++i) {
+            items.add("Foo " + i);
+        }
+        dummy.setDataSource(new LoggingDataSource(items));
+        dummy.select("Foo 200");
+
+        HorizontalLayout controls = new HorizontalLayout();
+        addComponent(controls);
+        controls.addComponent(new Button("Select Foo 20", e -> {
+            dummy.select("Foo " + 20);
+        }));
+        controls.addComponent(new Button("Reset data source", e -> {
+            dummy.setDataSource(new LoggingDataSource(items));
+        }));
+        addComponent(dummy);
+    }
+}
diff --git a/uitest/src/main/java/com/vaadin/tests/widgetset/client/data/DummyComponentConnector.java b/uitest/src/main/java/com/vaadin/tests/widgetset/client/data/DummyComponentConnector.java
new file mode 100644 (file)
index 0000000..061b52a
--- /dev/null
@@ -0,0 +1,43 @@
+package com.vaadin.tests.widgetset.client.data;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.vaadin.client.connectors.AbstractListingConnector;
+import com.vaadin.client.data.DataSource;
+import com.vaadin.client.ui.VLabel;
+import com.vaadin.shared.data.DataCommunicatorConstants;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.tests.data.DummyData.DummyComponent;
+
+import elemental.json.JsonObject;
+
+@Connect(DummyComponent.class)
+public class DummyComponentConnector extends AbstractListingConnector {
+
+    @Override
+    public FlowPanel getWidget() {
+        return (FlowPanel) super.getWidget();
+    }
+
+    @Override
+    public void setDataSource(DataSource<JsonObject> dataSource) {
+        super.setDataSource(dataSource);
+
+        dataSource.addDataChangeHandler(range -> {
+            assert range.getStart() == 0 && range.getEnd() == dataSource
+                    .size() : "Widget only supports full updates.";
+            getWidget().clear();
+            for (int i = range.getStart(); i < range.getEnd(); ++i) {
+                VLabel label = new VLabel();
+                getWidget().add(label);
+                JsonObject row = dataSource.getRow(i);
+                String text = row.getString(DataCommunicatorConstants.DATA);
+                if (row.hasKey(DataCommunicatorConstants.SELECTED)
+                        && row.getBoolean(DataCommunicatorConstants.SELECTED)) {
+                    text = "<b>" + text + "</b>";
+                    label.addStyleName("selected");
+                }
+                label.setHTML(text);
+            }
+        });
+    }
+}
index ed15f13f347ad890e7ef99627fb7c9f66abcb0bc..1b00d1405023cbcaf579ee2854830d506e053045 100644 (file)
@@ -30,7 +30,7 @@ import org.junit.Test;
 import org.openqa.selenium.NoSuchElementException;
 import org.openqa.selenium.WebElement;
 
-import com.vaadin.shared.ui.grid.Range;
+import com.vaadin.shared.Range;
 import com.vaadin.testbench.By;
 import com.vaadin.testbench.ElementQuery;
 import com.vaadin.testbench.TestBenchElement;
index f389b4b3f3c2f3b92585c612def37a5fc5fb55c9..93931d50ec1173bacc79a3ba8c420409d870a046 100644 (file)
@@ -33,7 +33,7 @@ import org.openqa.selenium.Keys;
 import org.openqa.selenium.WebElement;
 
 import com.vaadin.client.WidgetUtil;
-import com.vaadin.shared.ui.grid.Range;
+import com.vaadin.shared.Range;
 import com.vaadin.testbench.TestBenchElement;
 import com.vaadin.testbench.elements.NotificationElement;
 import com.vaadin.testbench.parallel.BrowserUtil;
diff --git a/uitest/src/test/java/com/vaadin/tests/data/DummyDataTest.java b/uitest/src/test/java/com/vaadin/tests/data/DummyDataTest.java
new file mode 100644 (file)
index 0000000..48001ef
--- /dev/null
@@ -0,0 +1,60 @@
+package com.vaadin.tests.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.testbench.By;
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+public class DummyDataTest extends SingleBrowserTest {
+
+    @Before
+    public void setUp() {
+        setDebug(true);
+        openTestURL();
+    }
+
+    @Test
+    public void testCorrectRowSelectedOnInit() {
+        List<WebElement> selected = findElements(By.className("selected"));
+        assertTrue("Only one should be selected", 1 == selected.size());
+        assertEquals("Wrong item selected", "Foo 200",
+                selected.get(0).getText());
+    }
+
+    @Test
+    public void testServerSelectionUpdatesSelected() {
+        $(ButtonElement.class).first().click();
+        List<WebElement> selected = findElements(By.className("selected"));
+        assertTrue("Only one should be selected", 1 == selected.size());
+        assertEquals("Wrong item selected", "Foo 20",
+                selected.get(0).getText());
+    }
+
+    @Test
+    public void testDataUpdateDoesNotCauseBackEndRequest() {
+        assertEquals("Unexpected backend requests", "2. Backend request #1",
+                getLogRow(0));
+        assertEquals("Unexpected backend requests", "1. Backend request #0",
+                getLogRow(1));
+        // Select a row on the server-side, triggers an update
+        $(ButtonElement.class).first().click();
+        assertEquals("No requests should have happened",
+                "2. Backend request #1", getLogRow(0));
+    }
+
+    @Test
+    public void testDataSourceChangeOnlyOneRequest() {
+        // Change to a new logging data source
+        $(ButtonElement.class).get(1).click();
+        assertEquals("DataSource change should only cause 1 request",
+                "3. Backend request #0", getLogRow(0));
+    }
+}