]> source.dussan.org Git - vaadin-framework.git/commitdiff
Added ItemSetAddEvent and ItemSetRemoveEvent (#2794)
authorJarno Rantala <jarno.rantala@vaadin.com>
Thu, 10 Oct 2013 10:08:09 +0000 (13:08 +0300)
committerVaadin Code Review <review@vaadin.com>
Wed, 16 Oct 2013 08:56:25 +0000 (08:56 +0000)
The events inherits the ItemSetChangedEvent and they contain more information about the added/removed items. These events are fired
from AbstractInMemoryContainer.

Change-Id: I0a7ddfd38fd01fa385479efc953ab444d1ecdf4c

server/src/com/vaadin/data/Container.java
server/src/com/vaadin/data/util/AbstractBeanContainer.java
server/src/com/vaadin/data/util/AbstractInMemoryContainer.java
server/src/com/vaadin/data/util/IndexedContainer.java
server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java
server/tests/src/com/vaadin/data/util/TestIndexedContainer.java

index e93db52a35c1b606f6befd58baa66d64911f6302..bf553f31d22985939495cd4a305ab87c6fbc85e7 100644 (file)
@@ -582,6 +582,60 @@ public interface Container extends Serializable {
         public Item addItemAt(int index, Object newItemId)
                 throws UnsupportedOperationException;
 
+        /**
+         * An <code>Event</code> object specifying information about the added
+         * items.
+         */
+        public interface ItemAddEvent extends ItemSetChangeEvent {
+
+            /**
+             * Gets the item id of the first added item.
+             * 
+             * @return item id of the first added item
+             */
+            public Object getFirstItemId();
+
+            /**
+             * Gets the index of the first added item.
+             * 
+             * @return index of the first added item
+             */
+            public int getFirstIndex();
+
+            /**
+             * Gets the number of the added items.
+             * 
+             * @return the number of added items.
+             */
+            public int getAddedItemsCount();
+        }
+
+        /**
+         * An <code>Event</code> object specifying information about the removed
+         * items.
+         */
+        public interface ItemRemoveEvent extends ItemSetChangeEvent {
+            /**
+             * Gets the item id of the first removed item.
+             * 
+             * @return item id of the first removed item
+             */
+            public Object getFirstItemId();
+
+            /**
+             * Gets the index of the first removed item.
+             * 
+             * @return index of the first removed item
+             */
+            public int getFirstIndex();
+
+            /**
+             * Gets the number of the removed items.
+             * 
+             * @return the number of removed items
+             */
+            public int getRemovedItemsCount();
+        }
     }
 
     /**
index cd5c0c809d57bf4e4df5cff18199d9ea84dd4513..67239996a23d8f4450ae25f4fa41709960fb3bda 100644 (file)
@@ -222,6 +222,7 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
     @Override
     public boolean removeAllItems() {
         int origSize = size();
+        IDTYPE firstItem = getFirstVisibleItem();
 
         internalRemoveAllItems();
 
@@ -234,7 +235,7 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
         // fire event only if the visible view changed, regardless of whether
         // filtered out items were removed or not
         if (origSize != 0) {
-            fireItemSetChange();
+            fireItemsRemoved(0, firstItem, origSize);
         }
 
         return true;
@@ -679,6 +680,8 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
     protected void addAll(Collection<? extends BEANTYPE> collection)
             throws IllegalStateException, IllegalArgumentException {
         boolean modified = false;
+        int origSize = size();
+
         for (BEANTYPE bean : collection) {
             // TODO skipping invalid beans - should not allow them in javadoc?
             if (bean == null
@@ -699,13 +702,22 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
         if (modified) {
             // Filter the contents when all items have been added
             if (isFiltered()) {
-                filterAll();
-            } else {
-                fireItemSetChange();
+                doFilterContainer(!getFilters().isEmpty());
+            }
+            if (visibleNewItemsWasAdded(origSize)) {
+                // fire event about added items
+                int firstPosition = origSize;
+                IDTYPE firstItemId = getVisibleItemIds().get(firstPosition);
+                int affectedItems = size() - origSize;
+                fireItemsAdded(firstPosition, firstItemId, affectedItems);
             }
         }
     }
 
+    private boolean visibleNewItemsWasAdded(int origSize) {
+        return size() > origSize;
+    }
+
     /**
      * Use the bean resolver to get the identifier for a bean.
      * 
index 84304431bcb09a514907c653c6187e95dcd4396e..9a7922b9286b102d6bcc4fe4443b1e3bf9d4d25e 100644 (file)
  */
 package com.vaadin.data.util;
 
+import java.io.Serializable;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.EventObject;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -146,6 +148,85 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
         }
     }
 
+    private static abstract class BaseItemAddOrRemoveEvent extends
+            EventObject implements Serializable {
+        protected Object itemId;
+        protected int index;
+        protected int count;
+
+        public BaseItemAddOrRemoveEvent(Container source, Object itemId,
+                int index, int count) {
+            super(source);
+            this.itemId = itemId;
+            this.index = index;
+            this.count = count;
+        }
+
+        public Container getContainer() {
+            return (Container) getSource();
+        }
+
+        public Object getFirstItemId() {
+            return itemId;
+        }
+
+        public int getFirstIndex() {
+            return index;
+        }
+
+        public int getAffectedItemsCount() {
+            return count;
+        }
+    }
+
+    /**
+     * An <code>Event</code> object specifying information about the added
+     * items.
+     * 
+     * <p>
+     * This class provides information about the first added item and the number
+     * of added items.
+     * </p>
+     */
+    protected static class BaseItemAddEvent extends
+            BaseItemAddOrRemoveEvent implements
+            Container.Indexed.ItemAddEvent {
+
+        public BaseItemAddEvent(Container source, Object itemId, int index,
+                int count) {
+            super(source, itemId, index, count);
+        }
+
+        @Override
+        public int getAddedItemsCount() {
+            return getAffectedItemsCount();
+        }
+    }
+
+    /**
+     * An <code>Event</code> object specifying information about the removed
+     * items.
+     * 
+     * <p>
+     * This class provides information about the first removed item and the
+     * number of removed items.
+     * </p>
+     */
+    protected static class BaseItemRemoveEvent extends
+            BaseItemAddOrRemoveEvent implements
+            Container.Indexed.ItemRemoveEvent {
+
+        public BaseItemRemoveEvent(Container source, Object itemId,
+                int index, int count) {
+            super(source, itemId, index, count);
+        }
+
+        @Override
+        public int getRemovedItemsCount() {
+            return getAffectedItemsCount();
+        }
+    }
+
     /**
      * Get an item even if filtered out.
      * 
@@ -898,36 +979,69 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
      * Notify item set change listeners that an item has been added to the
      * container.
      * 
-     * Unless subclasses specify otherwise, the default notification indicates a
-     * full refresh.
-     * 
      * @param postion
-     *            position of the added item in the view (if visible)
+     *            position of the added item in the view
      * @param itemId
      *            id of the added item
      * @param item
      *            the added item
      */
     protected void fireItemAdded(int position, ITEMIDTYPE itemId, ITEMCLASS item) {
-        fireItemSetChange();
+        fireItemsAdded(position, itemId, 1);
+    }
+
+    /**
+     * Notify item set change listeners that items has been added to the
+     * container.
+     * 
+     * @param firstPosition
+     *            position of the first visible added item in the view
+     * @param firstItemId
+     *            id of the first visible added item
+     * @param numberOfItems
+     *            the number of visible added items
+     */
+    protected void fireItemsAdded(int firstPosition, ITEMIDTYPE firstItemId,
+            int numberOfItems) {
+        BaseItemAddEvent addEvent = new BaseItemAddEvent(this,
+                firstItemId, firstPosition, numberOfItems);
+        fireItemSetChange(addEvent);
     }
 
     /**
      * Notify item set change listeners that an item has been removed from the
      * container.
      * 
-     * Unless subclasses specify otherwise, the default notification indicates a
-     * full refresh.
+     * @param position
+     *            position of the removed item in the view prior to removal
      * 
-     * @param postion
-     *            position of the removed item in the view prior to removal (if
-     *            was visible)
      * @param itemId
      *            id of the removed item, of type {@link Object} to satisfy
      *            {@link Container#removeItem(Object)} API
      */
     protected void fireItemRemoved(int position, Object itemId) {
-        fireItemSetChange();
+        fireItemsRemoved(position, itemId, 1);
+    }
+
+    /**
+     * Notify item set change listeners that items has been removed from the
+     * container.
+     * 
+     * @param firstPosition
+     *            position of the first visible removed item in the view prior
+     *            to removal
+     * @param firstItemId
+     *            id of the first visible removed item, of type {@link Object}
+     *            to satisfy {@link Container#removeItem(Object)} API
+     * @param numberOfItems
+     *            the number of removed visible items
+     * 
+     */
+    protected void fireItemsRemoved(int firstPosition, Object firstItemId,
+            int numberOfItems) {
+        BaseItemRemoveEvent removeEvent = new BaseItemRemoveEvent(this,
+                firstItemId, firstPosition, numberOfItems);
+        fireItemSetChange(removeEvent);
     }
 
     // visible and filtered item identifier lists
@@ -945,6 +1059,21 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
         }
     }
 
+    /**
+     * Returns the item id of the first visible item after filtering. 'Null' is
+     * returned if there is no visible items.
+     * 
+     * For internal use only.
+     * 
+     * @return item id of the first visible item
+     */
+    protected ITEMIDTYPE getFirstVisibleItem() {
+        if (!getVisibleItemIds().isEmpty()) {
+            return getVisibleItemIds().get(0);
+        }
+        return null;
+    }
+
     /**
      * Returns true is the container has active filters.
      * 
index d7bf70caf60ca4fbdc9846ce94fc8d1d2f0451b4..5d2091920843a51db92057082892e791955bf9c9 100644 (file)
@@ -226,6 +226,7 @@ public class IndexedContainer extends
     @Override
     public boolean removeAllItems() {
         int origSize = size();
+        Object firstItem = getFirstVisibleItem();
 
         internalRemoveAllItems();
 
@@ -235,7 +236,7 @@ public class IndexedContainer extends
         // filtered out items were removed or not
         if (origSize != 0) {
             // Sends a change event
-            fireItemSetChange();
+            fireItemsRemoved(0, firstItem, origSize);
         }
 
         return true;
@@ -620,8 +621,7 @@ public class IndexedContainer extends
     @Override
     protected void fireItemAdded(int position, Object itemId, Item item) {
         if (position >= 0) {
-            fireItemSetChange(new IndexedContainer.ItemSetChangeEvent(this,
-                    position));
+            super.fireItemAdded(position, itemId, item);
         }
     }
 
@@ -1211,4 +1211,5 @@ public class IndexedContainer extends
     public Collection<Filter> getContainerFilters() {
         return super.getContainerFilters();
     }
+
 }
index 3a2cb268b973957b36f121509afaef2afa04c171..35f09fc8f3033d2146cea4cd659a166c03b362f9 100644 (file)
@@ -10,8 +10,15 @@ import java.util.Map;
 
 import junit.framework.Assert;
 
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+
 import com.vaadin.data.Container;
+import com.vaadin.data.Container.Indexed.ItemAddEvent;
+import com.vaadin.data.Container.Indexed.ItemRemoveEvent;
+import com.vaadin.data.Container.ItemSetChangeListener;
 import com.vaadin.data.Item;
+import com.vaadin.data.util.filter.Compare;
 
 /**
  * Test basic functionality of BeanItemContainer.
@@ -737,4 +744,182 @@ public class BeanItemContainerTest extends AbstractBeanContainerTest {
             // should throw exception
         }
     }
+
+    public void testItemAddedEvent() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        Person bean = new Person("John");
+        ItemSetChangeListener addListener = createListenerMockFor(container);
+        addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class));
+        EasyMock.replay(addListener);
+
+        container.addItem(bean);
+
+        EasyMock.verify(addListener);
+    }
+
+    public void testItemAddedEvent_AddedItem() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        Person bean = new Person("John");
+        ItemSetChangeListener addListener = createListenerMockFor(container);
+        Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+        EasyMock.replay(addListener);
+
+        container.addItem(bean);
+
+        assertEquals(bean, capturedEvent.getValue().getFirstItemId());
+    }
+
+    public void testItemAddedEvent_addItemAt_IndexOfAddedItem() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        Person bean = new Person("John");
+        container.addItem(bean);
+        ItemSetChangeListener addListener = createListenerMockFor(container);
+        Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+        EasyMock.replay(addListener);
+
+        container.addItemAt(1, new Person(""));
+
+        assertEquals(1, capturedEvent.getValue().getFirstIndex());
+    }
+
+    public void testItemAddedEvent_addItemAfter_IndexOfAddedItem() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        Person bean = new Person("John");
+        container.addItem(bean);
+        ItemSetChangeListener addListener = createListenerMockFor(container);
+        Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+        EasyMock.replay(addListener);
+
+        container.addItemAfter(bean, new Person(""));
+
+        assertEquals(1, capturedEvent.getValue().getFirstIndex());
+    }
+
+    public void testItemAddedEvent_amountOfAddedItems() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        ItemSetChangeListener addListener = createListenerMockFor(container);
+        Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+        EasyMock.replay(addListener);
+        List<Person> beans = Arrays.asList(new Person("Jack"), new Person(
+                "John"));
+
+        container.addAll(beans);
+
+        assertEquals(2, capturedEvent.getValue().getAddedItemsCount());
+    }
+
+    public void testItemAddedEvent_someItemsAreFiltered_amountOfAddedItemsIsReducedByAmountOfFilteredItems() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        ItemSetChangeListener addListener = createListenerMockFor(container);
+        Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+        EasyMock.replay(addListener);
+        List<Person> beans = Arrays.asList(new Person("Jack"), new Person(
+                "John"));
+        container.addFilter(new Compare.Equal("name", "John"));
+
+        container.addAll(beans);
+
+        assertEquals(1, capturedEvent.getValue().getAddedItemsCount());
+    }
+
+    public void testItemAddedEvent_someItemsAreFiltered_addedItemIsTheFirstVisibleItem() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        Person bean = new Person("John");
+        ItemSetChangeListener addListener = createListenerMockFor(container);
+        Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+        EasyMock.replay(addListener);
+        List<Person> beans = Arrays.asList(new Person("Jack"), bean);
+        container.addFilter(new Compare.Equal("name", "John"));
+
+        container.addAll(beans);
+
+        assertEquals(bean, capturedEvent.getValue().getFirstItemId());
+    }
+
+    public void testItemRemovedEvent() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        Person bean = new Person("John");
+        container.addItem(bean);
+        ItemSetChangeListener removeListener = createListenerMockFor(container);
+        removeListener.containerItemSetChange(EasyMock
+                .isA(ItemRemoveEvent.class));
+        EasyMock.replay(removeListener);
+
+        container.removeItem(bean);
+
+        EasyMock.verify(removeListener);
+    }
+
+    public void testItemRemovedEvent_RemovedItem() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        Person bean = new Person("John");
+        container.addItem(bean);
+        ItemSetChangeListener removeListener = createListenerMockFor(container);
+        Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+        EasyMock.replay(removeListener);
+
+        container.removeItem(bean);
+
+        assertEquals(bean, capturedEvent.getValue().getFirstItemId());
+    }
+
+    public void testItemRemovedEvent_indexOfRemovedItem() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        container.addItem(new Person("Jack"));
+        Person secondBean = new Person("John");
+        container.addItem(secondBean);
+        ItemSetChangeListener removeListener = createListenerMockFor(container);
+        Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+        EasyMock.replay(removeListener);
+
+        container.removeItem(secondBean);
+
+        assertEquals(1, capturedEvent.getValue().getFirstIndex());
+    }
+
+    public void testItemRemovedEvent_amountOfRemovedItems() {
+        BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+                Person.class);
+        container.addItem(new Person("Jack"));
+        container.addItem(new Person("John"));
+        ItemSetChangeListener removeListener = createListenerMockFor(container);
+        Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+        EasyMock.replay(removeListener);
+
+        container.removeAllItems();
+
+        assertEquals(2, capturedEvent.getValue().getRemovedItemsCount());
+    }
+
+    private Capture<ItemAddEvent> captureAddEvent(
+            ItemSetChangeListener addListener) {
+        Capture<ItemAddEvent> capturedEvent = new Capture<ItemAddEvent>();
+        addListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+        return capturedEvent;
+    }
+
+    private Capture<ItemRemoveEvent> captureRemoveEvent(
+            ItemSetChangeListener removeListener) {
+        Capture<ItemRemoveEvent> capturedEvent = new Capture<ItemRemoveEvent>();
+        removeListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+        return capturedEvent;
+    }
+
+    private ItemSetChangeListener createListenerMockFor(
+            BeanItemContainer<Person> container) {
+        ItemSetChangeListener listener = EasyMock
+                .createNiceMock(ItemSetChangeListener.class);
+        container.addItemSetChangeListener(listener);
+        return listener;
+    }
 }
index 09e5a26c155269b45922f459d564dfb5d1be4d3d..5c7896509240f09534e2233b81f3d124b8df56ad 100644 (file)
@@ -4,6 +4,12 @@ import java.util.List;
 
 import junit.framework.Assert;
 
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+
+import com.vaadin.data.Container.Indexed.ItemAddEvent;
+import com.vaadin.data.Container.Indexed.ItemRemoveEvent;
+import com.vaadin.data.Container.ItemSetChangeListener;
 import com.vaadin.data.Item;
 
 public class TestIndexedContainer extends AbstractInMemoryContainerTest {
@@ -271,6 +277,113 @@ public class TestIndexedContainer extends AbstractInMemoryContainerTest {
         counter.assertNone();
     }
 
+    public void testItemAddedEvent() {
+        IndexedContainer container = new IndexedContainer();
+        ItemSetChangeListener addListener = createListenerMockFor(container);
+        addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class));
+        EasyMock.replay(addListener);
+
+        container.addItem();
+
+        EasyMock.verify(addListener);
+    }
+
+    public void testItemAddedEvent_AddedItem() {
+        IndexedContainer container = new IndexedContainer();
+        ItemSetChangeListener addListener = createListenerMockFor(container);
+        Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+        EasyMock.replay(addListener);
+
+        Object itemId = container.addItem();
+
+        assertEquals(itemId, capturedEvent.getValue().getFirstItemId());
+    }
+
+    public void testItemAddedEvent_IndexOfAddedItem() {
+        IndexedContainer container = new IndexedContainer();
+        ItemSetChangeListener addListener = createListenerMockFor(container);
+        container.addItem();
+        Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+        EasyMock.replay(addListener);
+
+        Object itemId = container.addItemAt(1);
+
+        assertEquals(1, capturedEvent.getValue().getFirstIndex());
+    }
+
+    public void testItemRemovedEvent() {
+        IndexedContainer container = new IndexedContainer();
+        Object itemId = container.addItem();
+        ItemSetChangeListener removeListener = createListenerMockFor(container);
+        removeListener.containerItemSetChange(EasyMock
+                .isA(ItemRemoveEvent.class));
+        EasyMock.replay(removeListener);
+
+        container.removeItem(itemId);
+
+        EasyMock.verify(removeListener);
+    }
+
+    public void testItemRemovedEvent_RemovedItem() {
+        IndexedContainer container = new IndexedContainer();
+        Object itemId = container.addItem();
+        ItemSetChangeListener removeListener = createListenerMockFor(container);
+        Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+        EasyMock.replay(removeListener);
+
+        container.removeItem(itemId);
+
+        assertEquals(itemId, capturedEvent.getValue().getFirstItemId());
+    }
+
+    public void testItemRemovedEvent_indexOfRemovedItem() {
+        IndexedContainer container = new IndexedContainer();
+        container.addItem();
+        Object secondItemId = container.addItem();
+        ItemSetChangeListener removeListener = createListenerMockFor(container);
+        Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+        EasyMock.replay(removeListener);
+
+        container.removeItem(secondItemId);
+
+        assertEquals(1, capturedEvent.getValue().getFirstIndex());
+    }
+
+    public void testItemRemovedEvent_amountOfRemovedItems() {
+        IndexedContainer container = new IndexedContainer();
+        container.addItem();
+        container.addItem();
+        ItemSetChangeListener removeListener = createListenerMockFor(container);
+        Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+        EasyMock.replay(removeListener);
+
+        container.removeAllItems();
+
+        assertEquals(2, capturedEvent.getValue().getRemovedItemsCount());
+    }
+
+    private Capture<ItemAddEvent> captureAddEvent(
+            ItemSetChangeListener addListener) {
+        Capture<ItemAddEvent> capturedEvent = new Capture<ItemAddEvent>();
+        addListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+        return capturedEvent;
+    }
+
+    private Capture<ItemRemoveEvent> captureRemoveEvent(
+            ItemSetChangeListener removeListener) {
+        Capture<ItemRemoveEvent> capturedEvent = new Capture<ItemRemoveEvent>();
+        removeListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+        return capturedEvent;
+    }
+
+    private ItemSetChangeListener createListenerMockFor(
+            IndexedContainer container) {
+        ItemSetChangeListener listener = EasyMock
+                .createNiceMock(ItemSetChangeListener.class);
+        container.addItemSetChangeListener(listener);
+        return listener;
+    }
+
     // Ticket 8028
     public void testGetItemIdsRangeIndexOutOfBounds() {
         IndexedContainer ic = new IndexedContainer();