]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fix RpcDataSource to use RPC for row pins/unpins (#13334)
authorTeemu Suo-Anttila <teemusa@vaadin.com>
Wed, 8 Oct 2014 14:30:13 +0000 (17:30 +0300)
committerVaadin Code Review <review@vaadin.com>
Wed, 22 Oct 2014 13:26:00 +0000 (13:26 +0000)
This patch removes the temprarilyPinnedRows workaround from
AbstractRemoteDataSource and refactors the whole feature to be part of
RpcDataSource where it should be.

Change-Id: Id55020dd11dda3dcf54dfe3c1b41af8e495c1c0c

client/src/com/vaadin/client/data/AbstractRemoteDataSource.java
client/src/com/vaadin/client/data/RpcDataSourceConnector.java
client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java
client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java
server/src/com/vaadin/data/RpcDataProviderExtension.java
server/src/com/vaadin/ui/components/grid/Grid.java
shared/src/com/vaadin/shared/data/DataRequestRpc.java

index 45e15336624b0539f7062f54513b20a42eaf93ab..5052b9019be1e1c394e54e529555251d7529d194 100644 (file)
@@ -76,32 +76,18 @@ public abstract class AbstractRemoteDataSource<T> implements DataSource<T> {
             }
         }
 
-        private boolean isPinned() {
+        protected boolean isPinned() {
             return pinnedRows.containsKey(key);
         }
 
         @Override
         public void pin() {
-            Integer count = pinnedCounts.get(key);
-            if (count == null) {
-                count = Integer.valueOf(0);
-                pinnedRows.put(key, this);
-            }
-            pinnedCounts.put(key, Integer.valueOf(count.intValue() + 1));
+            pinHandle(this);
         }
 
         @Override
         public void unpin() throws IllegalStateException {
-            final Integer count = pinnedCounts.get(key);
-            if (count == null) {
-                throw new IllegalStateException("Row " + row + " with key "
-                        + key + " was not pinned to begin with");
-            } else if (count.equals(Integer.valueOf(1))) {
-                pinnedRows.remove(key);
-                pinnedCounts.remove(key);
-            } else {
-                pinnedCounts.put(key, Integer.valueOf(count.intValue() - 1));
-            }
+            unpinHandle(this);
         }
 
         @Override
@@ -174,6 +160,48 @@ public abstract class AbstractRemoteDataSource<T> implements DataSource<T> {
         }
     }
 
+    /**
+     * Pins a row with given handle. This function can be overridden to do
+     * specific logic related to pinning rows.
+     * 
+     * @param handle
+     *            row handle to pin
+     */
+    protected void pinHandle(RowHandleImpl handle) {
+        Object key = handle.key;
+        Integer count = pinnedCounts.get(key);
+        if (count == null) {
+            count = Integer.valueOf(0);
+            pinnedRows.put(key, handle);
+        }
+        pinnedCounts.put(key, Integer.valueOf(count.intValue() + 1));
+    }
+
+    /**
+     * Unpins a previously pinned row with given handle. This function can be
+     * overridden to do specific logic related to unpinning rows.
+     * 
+     * @param handle
+     *            row handle to unpin
+     * 
+     * @throws IllegalStateException
+     *             if given row handle has not been pinned before
+     */
+    protected void unpinHandle(RowHandleImpl handle)
+            throws IllegalStateException {
+        Object key = handle.key;
+        final Integer count = pinnedCounts.get(key);
+        if (count == null) {
+            throw new IllegalStateException("Row " + handle.getRow()
+                    + " with key " + key + " was not pinned to begin with");
+        } else if (count.equals(Integer.valueOf(1))) {
+            pinnedRows.remove(key);
+            pinnedCounts.remove(key);
+        } else {
+            pinnedCounts.put(key, Integer.valueOf(count.intValue() - 1));
+        }
+    }
+
     @Override
     public void ensureAvailability(int firstRowIndex, int numberOfRows) {
         requestedAvailability = Range.withLength(firstRowIndex, numberOfRows);
@@ -556,35 +584,6 @@ public abstract class AbstractRemoteDataSource<T> implements DataSource<T> {
      */
     abstract public Object getRowKey(T row);
 
-    /**
-     * Marks rows as pinned when fetching new rows.
-     * <p>
-     * This collection of rows are intended to remain pinned if new rows are
-     * fetched from the data source, even if some of the pinned rows would fall
-     * off the cache and become inactive.
-     * <p>
-     * This method does nothing by itself, other than it stores the rows into a
-     * field. The implementation needs to make all the adjustments for itself.
-     * Check {@link RpcDataSourceConnector.RpcDataSource#requestRows(int, int)}
-     * for an implementation example.
-     * 
-     * @param keys
-     *            a collection of rows to keep pinned
-     * 
-     * @see #temporarilyPinnedRows
-     * @see RpcDataSourceConnector.RpcDataSource#requestRows(int, int)
-     * @deprecated You probably don't want to call this method unless you're
-     *             writing a Renderer for a selection model. Even if you are, be
-     *             very aware what this method does and how it behaves.
-     */
-    @Deprecated
-    public void transactionPin(Collection<T> rows) {
-        if (rows == null) {
-            throw new IllegalArgumentException("argument may not be null");
-        }
-        temporarilyPinnedRows = rows;
-    }
-
     protected void resetDataAndSize(int newSize) {
         dropFromCache(getCachedRange());
         cached = Range.withLength(0, 0);
index deb3b5ed7c308b3e5bd2360f28d9c2243afef77b..cc1e294fb82d7b0df1b2b5d3dbebbfe460fd4e85 100644 (file)
@@ -17,9 +17,6 @@
 package com.vaadin.client.data;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
 
 import com.google.gwt.json.client.JSONArray;
 import com.google.gwt.json.client.JSONObject;
@@ -50,31 +47,18 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector {
 
     public class RpcDataSource extends AbstractRemoteDataSource<JSONObject> {
 
-        private Collection<JSONObject> prevRows = Collections.emptySet();
+        private DataRequestRpc rpcProxy = getRpcProxy(DataRequestRpc.class);
 
         @Override
         protected void requestRows(int firstRowIndex, int numberOfRows) {
             Range cached = getCachedRange();
 
-            Collection<JSONObject> newRows = new ArrayList<JSONObject>(
-                    temporarilyPinnedRows);
-            newRows.removeAll(prevRows);
-
-            List<String> temporarilyPinnedKeys = new ArrayList<String>(
-                    newRows.size());
-            for (JSONObject row : newRows) {
-                temporarilyPinnedKeys.add((String) getRowKey(row));
-            }
-
-            getRpcProxy(DataRequestRpc.class).requestRows(firstRowIndex,
-                    numberOfRows, cached.getStart(), cached.length(),
-                    temporarilyPinnedKeys);
-
-            prevRows = temporarilyPinnedRows;
+            rpcProxy.requestRows(firstRowIndex, numberOfRows,
+                    cached.getStart(), cached.length());
         }
 
         @Override
-        public Object getRowKey(JSONObject row) {
+        public String getRowKey(JSONObject row) {
             JSONString string = row.get(GridState.JSONKEY_ROWKEY).isString();
             if (string != null) {
                 return string.stringValue();
@@ -90,19 +74,30 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector {
         }
 
         @Override
-        @SuppressWarnings("deprecation")
-        public void transactionPin(Collection<JSONObject> keys) {
-            super.transactionPin(keys);
-            if (keys.isEmpty() && !prevRows.isEmpty()) {
-                prevRows = Collections.emptySet();
-                getRpcProxy(DataRequestRpc.class)
-                        .releaseTemporarilyPinnedKeys();
+        public int size() {
+            return getState().containerSize;
+        }
+
+        @Override
+        protected void pinHandle(RowHandleImpl handle) {
+            // Server only knows if something is pinned or not. No need to pin
+            // multiple times.
+            boolean pinnedBefore = handle.isPinned();
+            super.pinHandle(handle);
+            if (!pinnedBefore) {
+                rpcProxy.setPinned(getRowKey(handle.getRow()), true);
             }
         }
 
         @Override
-        public int size() {
-            return getState().containerSize;
+        protected void unpinHandle(RowHandleImpl handle) {
+            // Row data is no longer available after it has been unpinned.
+            String key = getRowKey(handle.getRow());
+            super.unpinHandle(handle);
+            if (!handle.isPinned()) {
+                rpcProxy.setPinned(key, false);
+            }
+
         }
     }
 
index 5228a466a2098d44084d54364f0395e947ae3b7f..b059beca545f39f24c27e6c0c5385e57a785082d 100644 (file)
@@ -16,7 +16,6 @@
 package com.vaadin.client.ui.grid.selection;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashSet;
 
 import com.google.gwt.animation.client.AnimationScheduler;
@@ -34,8 +33,6 @@ import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.Event.NativePreviewEvent;
 import com.google.gwt.user.client.Event.NativePreviewHandler;
 import com.vaadin.client.Util;
-import com.vaadin.client.data.AbstractRemoteDataSource;
-import com.vaadin.client.data.DataSource;
 import com.vaadin.client.ui.grid.Cell;
 import com.vaadin.client.ui.grid.DataAvailableEvent;
 import com.vaadin.client.ui.grid.DataAvailableHandler;
@@ -231,9 +228,6 @@ public class MultiSelectionRenderer<T> extends ComplexRenderer<Boolean> {
 
         private boolean scrollAreaShouldRebound = false;
 
-        private AbstractRemoteDataSource<T> remoteDataSource = null;
-        private Batched<T> batchedSelectionModel = null;
-
         public AutoScrollerAndSelector(final int topBound,
                 final int bottomBound, final int gradientArea,
                 final boolean selectionPaint) {
@@ -258,20 +252,11 @@ public class MultiSelectionRenderer<T> extends ComplexRenderer<Boolean> {
                 grid.setScrollTop(grid.getScrollTop() + intPixelsToScroll);
             }
 
-            @SuppressWarnings("hiding")
             int logicalRow = getLogicalRowIndex(Util.getElementFromPoint(pageX,
                     pageY));
             if (logicalRow != -1 && logicalRow != this.logicalRow) {
                 this.logicalRow = logicalRow;
                 setSelected(logicalRow, selectionPaint);
-
-                if (remoteDataSource != null && batchedSelectionModel != null) {
-                    Collection<T> pinneds = batchedSelectionModel
-                            .getSelectedRowsBatch();
-                    pinneds.addAll(batchedSelectionModel
-                            .getDeselectedRowsBatch());
-                    remoteDataSource.transactionPin(pinneds);
-                }
             }
 
             reschedule();
@@ -319,41 +304,17 @@ public class MultiSelectionRenderer<T> extends ComplexRenderer<Boolean> {
             scrollSpeed = ratio * SCROLL_TOP_SPEED_PX_SEC;
         }
 
-        @SuppressWarnings("deprecation")
         public void start(int logicalRowIndex) {
             running = true;
             setSelected(logicalRowIndex, selectionPaint);
             logicalRow = logicalRowIndex;
             reschedule();
-
-            DataSource<T> dataSource = grid.getDataSource();
-            SelectionModel<T> selectionModel = grid.getSelectionModel();
-            if (dataSource instanceof AbstractRemoteDataSource
-                    && selectionModel instanceof Batched) {
-                this.remoteDataSource = (AbstractRemoteDataSource<T>) dataSource;
-                this.batchedSelectionModel = (Batched<T>) selectionModel;
-
-                Collection<T> pinneds = batchedSelectionModel
-                        .getSelectedRowsBatch();
-                pinneds.addAll(batchedSelectionModel.getDeselectedRowsBatch());
-                remoteDataSource.transactionPin(pinneds);
-            }
         }
 
         @SuppressWarnings("deprecation")
         public void stop() {
             running = false;
 
-            if (remoteDataSource != null) {
-                // split into two lines because of Java generics not playing
-                // nice.
-                @SuppressWarnings("unchecked")
-                Collection<T> emptySet = (Collection<T>) Collections.emptySet();
-                remoteDataSource.transactionPin(emptySet);
-                remoteDataSource = null;
-                batchedSelectionModel = null;
-            }
-
             if (handle != null) {
                 handle.cancel();
                 handle = null;
index b6ecc945e23e2d0cb9eb2d03483b28bb47867c90..33b5354570ea1ea60e369d38669ddad457752fd8 100644 (file)
@@ -183,6 +183,7 @@ public class SelectionModelMulti<T> extends AbstractRowHandleSelectionModel<T>
     @Override
     protected boolean deselectByHandle(RowHandle<T> handle) {
         if (selectedRows.remove(handle)) {
+
             if (!isBeingBatchSelected()) {
                 handle.unpin();
             } else {
@@ -227,6 +228,11 @@ public class SelectionModelMulti<T> extends AbstractRowHandleSelectionModel<T>
         selectionBatch.clear();
 
         final Collection<T> removed = getDeselectedRowsBatch();
+
+        // unpin deselected rows
+        for (RowHandle<T> handle : deselectionBatch) {
+            handle.unpin();
+        }
         deselectionBatch.clear();
 
         grid.fireEvent(new SelectionChangeEvent<T>(grid, added, removed,
index 99ff57089ccd8ea3558978dba15ded98ac2d7cbd..68f40f6cf3816ec500dbcfe80a5ab6334d59fe08 100644 (file)
@@ -49,8 +49,6 @@ import com.vaadin.shared.ui.grid.Range;
 import com.vaadin.ui.components.grid.Grid;
 import com.vaadin.ui.components.grid.GridColumn;
 import com.vaadin.ui.components.grid.Renderer;
-import com.vaadin.ui.components.grid.selection.SelectionChangeEvent;
-import com.vaadin.ui.components.grid.selection.SelectionChangeListener;
 
 import elemental.json.Json;
 import elemental.json.JsonArray;
@@ -650,20 +648,9 @@ public class RpcDataProviderExtension extends AbstractExtension {
         rpc = getRpcProxy(DataProviderRpc.class);
 
         registerRpc(new DataRequestRpc() {
-            private Collection<String> allTemporarilyPinnedKeys = new ArrayList<String>();
-
             @Override
             public void requestRows(int firstRow, int numberOfRows,
-                    int firstCachedRowIndex, int cacheSize,
-                    List<String> temporarilyPinnedKeys) {
-
-                for (String key : temporarilyPinnedKeys) {
-                    Object itemId = keyMapper.getItemId(key);
-                    if (!keyMapper.isPinned(itemId)) {
-                        keyMapper.pin(itemId);
-                    }
-                }
-                allTemporarilyPinnedKeys.addAll(temporarilyPinnedKeys);
+                    int firstCachedRowIndex, int cacheSize) {
 
                 Range active = Range.withLength(firstRow, numberOfRows);
                 if (cacheSize != 0) {
@@ -682,52 +669,12 @@ public class RpcDataProviderExtension extends AbstractExtension {
             }
 
             @Override
-            public void releaseTemporarilyPinnedKeys() {
-                /*
-                 * This needs to be done deferredly since the selection event
-                 * comes after this RPC call.
-                 */
-
-                final SelectionChangeListener listener = new SelectionChangeListener() {
-                    @Override
-                    public void selectionChange(SelectionChangeEvent event) {
-                        for (String tempPinnedKey : allTemporarilyPinnedKeys) {
-                            /*
-                             * TODO: this could be moved into a field instead of
-                             * inline to reduce indentations.
-                             */
-
-                            /*
-                             * This works around the fact that when deselecting
-                             * and leaping through the cache, the client tries
-                             * to send a deselect event even though a row never
-                             * was selected. So, it tries to unpin something
-                             * that never was temporarily pinned.
-                             * 
-                             * If the same thing would happen while selecting
-                             * (instead of deselecting), the row would be
-                             * pinned, not because of the temporary pinning, but
-                             * because it's selected.
-                             */
-                            if (!keyMapper.isPinned(tempPinnedKey)) {
-                                continue;
-                            }
-
-                            Object itemId = keyMapper.getItemId(tempPinnedKey);
-                            Integer index = keyMapper.indexToItemId.inverse()
-                                    .get(itemId);
-                            if (!getGrid().isSelected(itemId)
-                                    && !activeRowHandler.activeRange
-                                            .contains(index.intValue())) {
-                                keyMapper.unpin(itemId);
-                            }
-                        }
-                        allTemporarilyPinnedKeys = new ArrayList<String>();
-                        getGrid().removeSelectionChangeListener(this);
-                    }
-                };
-
-                getGrid().addSelectionChangeListener(listener);
+            public void setPinned(String key, boolean isPinned) {
+                if (isPinned) {
+                    keyMapper.pin(keyMapper.getItemId(key));
+                } else {
+                    keyMapper.unpin(keyMapper.getItemId(key));
+                }
             }
         });
 
index fe03d589c08ddb1f30eb36e2cb3e24841d52dc98..8203f9c344806f55cd7eaedb11fb194fd577a7c5 100644 (file)
@@ -305,9 +305,18 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier,
         addSelectionChangeListener(new SelectionChangeListener() {
             @Override
             public void selectionChange(SelectionChangeEvent event) {
-                for (Object removedItemId : event.getRemoved()) {
-                    getKeyMapper().unpin(removedItemId);
-                }
+                /*
+                 * This listener nor anything else in the server side should
+                 * never unpin anything from KeyMapper. Pinning is mostly a
+                 * client feature and is only used when selecting something from
+                 * the server side. This is to ensure that client has the
+                 * correct key from server when the selected row is first
+                 * loaded.
+                 * 
+                 * Once client has gotten info that it is supposed to select a
+                 * row, it will pin the data from the client side as well and it
+                 * will be unpinned once it gets deselected.
+                 */
 
                 for (Object addedItemId : event.getAdded()) {
                     if (!getKeyMapper().isPinned(addedItemId)) {
index 000d196be977f921948793a0d3f675ff4625f898..637a3534470a9cd72b8d51fb273696e0d4f7f6fc 100644 (file)
@@ -16,8 +16,7 @@
 
 package com.vaadin.shared.data;
 
-import java.util.List;
-
+import com.vaadin.shared.annotations.Delayed;
 import com.vaadin.shared.communication.ServerRpc;
 
 /**
@@ -39,17 +38,20 @@ public interface DataRequestRpc extends ServerRpc {
      *            the index of the first cached row
      * @param cacheSize
      *            the number of cached rows
-     * @param temporarilyPinnedKeys
-     *            the keys that should remain pinned, even if some of these
-     *            would fall out of the cache range
      */
     public void requestRows(int firstRowIndex, int numberOfRows,
-            int firstCachedRowIndex, int cacheSize,
-            List<String> temporarilyPinnedKeys);
+            int firstCachedRowIndex, int cacheSize);
 
     /**
-     * Informs the back-end that the temporarily pinned keys in
-     * {@link #requestRows(int, int, int, int, List)} may be released.
+     * Informs the server that an item referenced with a key pinned status has
+     * changed. This is a delayed call that happens along with next rpc call to
+     * server.
+     * 
+     * @param key
+     *            key mapping to item
+     * @param isPinned
+     *            pinned status of referenced item
      */
-    public void releaseTemporarilyPinnedKeys();
+    @Delayed
+    public void setPinned(String key, boolean isPinned);
 }