| /* |
| * Copyright 2000-2014 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 org.eclipse.osbp.runtime.web.vaadin.common.data; |
| |
| 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; |
| import java.util.List; |
| import java.util.Set; |
| |
| import com.vaadin.data.Container; |
| import com.vaadin.data.Container.ItemSetChangeNotifier; |
| import com.vaadin.data.Item; |
| import com.vaadin.data.util.AbstractContainer; |
| import com.vaadin.data.util.DefaultItemSorter; |
| import com.vaadin.data.util.ItemSorter; |
| import com.vaadin.data.util.ListSet; |
| import com.vaadin.data.util.filter.SimpleStringFilter; |
| import com.vaadin.data.util.filter.UnsupportedFilterException; |
| |
| /** |
| * Abstract {@link Container} class that handles common functionality for |
| * in-memory containers. Concrete in-memory container classes can either inherit |
| * this class, inherit {@link AbstractContainer}, or implement the |
| * {@link Container} interface directly. |
| * |
| * Adding and removing items (if desired) must be implemented in subclasses by |
| * overriding the appropriate add*Item() and remove*Item() and removeAllItems() |
| * methods, calling the corresponding |
| * {@link #internalAddItemAfter(Object, Object, Item)}, |
| * {@link #internalAddItemAt(int, Object, Item)}, |
| * {@link #internalAddItemAtEnd(Object, Item, boolean)}, |
| * {@link #internalRemoveItem(Object)} and {@link #internalRemoveAllItems()} |
| * methods. |
| * |
| * By default, adding and removing container properties is not supported, and |
| * subclasses need to implement {@link #getContainerPropertyIds()}. Optionally, |
| * subclasses can override {@link #addContainerProperty(Object, Class, Object)} |
| * and {@link #removeContainerProperty(Object)} to implement them. |
| * |
| * Features: |
| * <ul> |
| * <li>{@link Container.Ordered} |
| * <li>{@link Container.Indexed} |
| * <li>{@link Filterable} and {@link SimpleFilterable} (internal implementation, |
| * does not implement the interface directly) |
| * <li>{@link Sortable} (internal implementation, does not implement the |
| * interface directly) |
| * </ul> |
| * |
| * To implement {@link Sortable}, subclasses need to implement |
| * {@link #getSortablePropertyIds()} and call the superclass method |
| * {@link #sortContainer(Object[], boolean[])} in the method |
| * <code>sort(Object[], boolean[])</code>. |
| * |
| * To implement {@link Filterable}, subclasses need to implement the methods |
| * {@link Filterable#addContainerFilter(com.vaadin.data.Container.Filter)} |
| * (calling {@link #addFilter(Filter)}), |
| * {@link Filterable#removeAllContainerFilters()} (calling |
| * {@link #removeAllFilters()}) and |
| * {@link Filterable#removeContainerFilter(com.vaadin.data.Container.Filter)} |
| * (calling {@link #removeFilter(com.vaadin.data.Container.Filter)}). |
| * |
| * To implement {@link SimpleFilterable}, subclasses also need to implement the |
| * methods |
| * {@link SimpleFilterable#addContainerFilter(Object, String, boolean, boolean)} |
| * and {@link SimpleFilterable#removeContainerFilters(Object)} calling |
| * {@link #addFilter(com.vaadin.data.Container.Filter)} and |
| * {@link #removeFilters(Object)} respectively. |
| * |
| * @param <ITEMIDTYPE> |
| * the class of item identifiers in the container, use Object if can |
| * be any class |
| * @param <PROPERTYIDCLASS> |
| * the class of property identifiers for the items in the container, |
| * use Object if can be any class |
| * @param <ITEMCLASS> |
| * the (base) class of the Item instances in the container, use |
| * {@link Item} if unknown |
| * |
| * @since 6.6 |
| */ |
| public abstract class AbstractInMemoryContainerCustom<ITEMIDTYPE, PROPERTYIDCLASS, ITEMCLASS extends Item> |
| extends AbstractContainer |
| implements ItemSetChangeNotifier, Container.Indexed { |
| |
| /** |
| * An ordered {@link List} of all item identifiers in the container, |
| * including those that have been filtered out. |
| * |
| * Must not be null. |
| */ |
| private List<ITEMIDTYPE> allItemIds; |
| |
| /** |
| * An ordered {@link List} of item identifiers in the container after |
| * filtering, excluding those that have been filtered out. |
| * |
| * This is what the external API of the {@link Container} interface and its |
| * subinterfaces shows (e.g. {@link #size()}, {@link #nextItemId(Object)}). |
| * |
| * If null, the full item id list is used instead. |
| */ |
| private List<ITEMIDTYPE> filteredItemIds; |
| |
| /** |
| * Filters that are applied to the container to limit the items visible in |
| * it |
| */ |
| private Set<Filter> filters = new HashSet<Filter>(); |
| |
| /** |
| * The item sorter which is used for sorting the container. |
| */ |
| private ItemSorter itemSorter = new DefaultItemSorter(); |
| |
| // Constructors |
| |
| /** |
| * Constructor for an abstract in-memory container. |
| */ |
| protected AbstractInMemoryContainerCustom() { |
| setAllItemIds(new ListSet<ITEMIDTYPE>()); |
| } |
| |
| // Container interface methods with more specific return class |
| |
| // default implementation, can be overridden |
| @Override |
| public ITEMCLASS getItem(Object itemId) { |
| if (containsId(itemId)) { |
| return getUnfilteredItem(itemId); |
| } else { |
| return null; |
| } |
| } |
| |
| 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> |
| * |
| * @since 7.4 |
| */ |
| 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> |
| * |
| * @since 7.4 |
| */ |
| 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. |
| * |
| * For internal use only. |
| * |
| * @param itemId |
| * @return |
| */ |
| protected abstract ITEMCLASS getUnfilteredItem(Object itemId); |
| |
| // cannot override getContainerPropertyIds() and getItemIds(): if subclass |
| // uses Object as ITEMIDCLASS or PROPERTYIDCLASS, Collection<Object> cannot |
| // be cast to Collection<MyInterface> |
| |
| // public abstract Collection<PROPERTYIDCLASS> getContainerPropertyIds(); |
| // public abstract Collection<ITEMIDCLASS> getItemIds(); |
| |
| // Container interface method implementations |
| |
| @Override |
| public int size() { |
| return getVisibleItemIds().size(); |
| } |
| |
| @Override |
| public boolean containsId(Object itemId) { |
| // only look at visible items after filtering |
| if (itemId == null) { |
| return false; |
| } else { |
| return getVisibleItemIds().contains(itemId); |
| } |
| } |
| |
| @Override |
| public List<?> getItemIds() { |
| return Collections.unmodifiableList(getVisibleItemIds()); |
| } |
| |
| // Container.Ordered |
| |
| @Override |
| public ITEMIDTYPE nextItemId(Object itemId) { |
| int index = indexOfId(itemId); |
| if (index >= 0 && index < size() - 1) { |
| return getIdByIndex(index + 1); |
| } else { |
| // out of bounds |
| return null; |
| } |
| } |
| |
| @Override |
| public ITEMIDTYPE prevItemId(Object itemId) { |
| int index = indexOfId(itemId); |
| if (index > 0) { |
| return getIdByIndex(index - 1); |
| } else { |
| // out of bounds |
| return null; |
| } |
| } |
| |
| @Override |
| public ITEMIDTYPE firstItemId() { |
| if (size() > 0) { |
| return getIdByIndex(0); |
| } else { |
| return null; |
| } |
| } |
| |
| @Override |
| public ITEMIDTYPE lastItemId() { |
| if (size() > 0) { |
| return getIdByIndex(size() - 1); |
| } else { |
| return null; |
| } |
| } |
| |
| @Override |
| public boolean isFirstId(Object itemId) { |
| if (itemId == null) { |
| return false; |
| } |
| return itemId.equals(firstItemId()); |
| } |
| |
| @Override |
| public boolean isLastId(Object itemId) { |
| if (itemId == null) { |
| return false; |
| } |
| return itemId.equals(lastItemId()); |
| } |
| |
| // Container.Indexed |
| |
| @Override |
| public ITEMIDTYPE getIdByIndex(int index) { |
| return getVisibleItemIds().get(index); |
| } |
| |
| @Override |
| public List<ITEMIDTYPE> getItemIds(int startIndex, int numberOfIds) { |
| if (startIndex < 0) { |
| throw new IndexOutOfBoundsException( |
| "Start index cannot be negative! startIndex=" + startIndex); |
| } |
| |
| if (startIndex > getVisibleItemIds().size()) { |
| throw new IndexOutOfBoundsException( |
| "Start index exceeds container size! startIndex=" |
| + startIndex + " containerLastItemIndex=" |
| + (getVisibleItemIds().size() - 1)); |
| } |
| |
| if (numberOfIds < 1) { |
| if (numberOfIds == 0) { |
| return Collections.emptyList(); |
| } |
| |
| throw new IllegalArgumentException( |
| "Cannot get negative amount of items! numberOfItems=" |
| + numberOfIds); |
| } |
| |
| int endIndex = startIndex + numberOfIds; |
| |
| if (endIndex > getVisibleItemIds().size()) { |
| endIndex = getVisibleItemIds().size(); |
| } |
| |
| return Collections.unmodifiableList( |
| getVisibleItemIds().subList(startIndex, endIndex)); |
| |
| } |
| |
| @Override |
| public int indexOfId(Object itemId) { |
| return getVisibleItemIds().indexOf(itemId); |
| } |
| |
| // methods that are unsupported by default, override to support |
| |
| @Override |
| public Object addItemAt(int index) throws UnsupportedOperationException { |
| throw new UnsupportedOperationException( |
| "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); |
| } |
| |
| @Override |
| public Item addItemAt(int index, Object newItemId) |
| throws UnsupportedOperationException { |
| throw new UnsupportedOperationException( |
| "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); |
| } |
| |
| @Override |
| public Object addItemAfter(Object previousItemId) |
| throws UnsupportedOperationException { |
| throw new UnsupportedOperationException( |
| "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); |
| } |
| |
| @Override |
| public Item addItemAfter(Object previousItemId, Object newItemId) |
| throws UnsupportedOperationException { |
| throw new UnsupportedOperationException( |
| "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); |
| } |
| |
| @Override |
| public Item addItem(Object itemId) throws UnsupportedOperationException { |
| throw new UnsupportedOperationException( |
| "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); |
| } |
| |
| @Override |
| public Object addItem() throws UnsupportedOperationException { |
| throw new UnsupportedOperationException( |
| "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); |
| } |
| |
| @Override |
| public boolean removeItem(Object itemId) |
| throws UnsupportedOperationException { |
| throw new UnsupportedOperationException( |
| "Removing items not supported. Override the removeItem() method if required as specified in AbstractInMemoryContainer javadoc."); |
| } |
| |
| @Override |
| public boolean removeAllItems() throws UnsupportedOperationException { |
| throw new UnsupportedOperationException( |
| "Removing items not supported. Override the removeAllItems() method if required as specified in AbstractInMemoryContainer javadoc."); |
| } |
| |
| @Override |
| public boolean addContainerProperty(Object propertyId, Class<?> type, |
| Object defaultValue) throws UnsupportedOperationException { |
| throw new UnsupportedOperationException( |
| "Adding container properties not supported. Override the addContainerProperty() method if required."); |
| } |
| |
| @Override |
| public boolean removeContainerProperty(Object propertyId) |
| throws UnsupportedOperationException { |
| throw new UnsupportedOperationException( |
| "Removing container properties not supported. Override the addContainerProperty() method if required."); |
| } |
| |
| // ItemSetChangeNotifier |
| /** |
| * @deprecated As of 7.0, replaced by |
| * {@link #addItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} |
| **/ |
| @Deprecated |
| @Override |
| public void addListener(Container.ItemSetChangeListener listener) { |
| addItemSetChangeListener(listener); |
| } |
| |
| @Override |
| public void addItemSetChangeListener( |
| Container.ItemSetChangeListener listener) { |
| super.addItemSetChangeListener(listener); |
| } |
| |
| @Override |
| public void removeItemSetChangeListener( |
| Container.ItemSetChangeListener listener) { |
| super.removeItemSetChangeListener(listener); |
| } |
| |
| /** |
| * @deprecated As of 7.0, replaced by |
| * {@link #removeItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} |
| **/ |
| @Deprecated |
| @Override |
| public void removeListener(Container.ItemSetChangeListener listener) { |
| removeItemSetChangeListener(listener); |
| } |
| |
| // internal methods |
| |
| // Filtering support |
| |
| /** |
| * Filter the view to recreate the visible item list from the unfiltered |
| * items, and send a notification if the set of visible items changed in any |
| * way. |
| */ |
| protected void filterAll() { |
| if (doFilterContainer(!getFilters().isEmpty())) { |
| fireItemSetChange(); |
| } |
| } |
| |
| /** |
| * Filters the data in the container and updates internal data structures. |
| * This method should reset any internal data structures and then repopulate |
| * them so {@link #getItemIds()} and other methods only return the filtered |
| * items. |
| * |
| * @param hasFilters |
| * true if filters has been set for the container, false |
| * otherwise |
| * @return true if the item set has changed as a result of the filtering |
| */ |
| protected boolean doFilterContainer(boolean hasFilters) { |
| if (!hasFilters) { |
| boolean changed = getAllItemIds().size() != getVisibleItemIds() |
| .size(); |
| setFilteredItemIds(null); |
| return changed; |
| } |
| |
| // Reset filtered list |
| List<ITEMIDTYPE> originalFilteredItemIds = getFilteredItemIds(); |
| boolean wasUnfiltered = false; |
| if (originalFilteredItemIds == null) { |
| originalFilteredItemIds = Collections.emptyList(); |
| wasUnfiltered = true; |
| } |
| setFilteredItemIds(new ListSet<ITEMIDTYPE>()); |
| |
| // Filter |
| boolean equal = true; |
| Iterator<ITEMIDTYPE> origIt = originalFilteredItemIds.iterator(); |
| for (final Iterator<ITEMIDTYPE> i = getAllItemIds().iterator(); i |
| .hasNext();) { |
| final ITEMIDTYPE id = i.next(); |
| if (passesFilters(id)) { |
| // filtered list comes from the full list, can use == |
| equal = equal && origIt.hasNext() && origIt.next() == id; |
| getFilteredItemIds().add(id); |
| } |
| } |
| |
| return (wasUnfiltered && !getAllItemIds().isEmpty()) || !equal |
| || origIt.hasNext(); |
| } |
| |
| /** |
| * Checks if the given itemId passes the filters set for the container. The |
| * caller should make sure the itemId exists in the container. For |
| * non-existing itemIds the behavior is undefined. |
| * |
| * @param itemId |
| * An itemId that exists in the container. |
| * @return true if the itemId passes all filters or no filters are set, |
| * false otherwise. |
| */ |
| protected boolean passesFilters(Object itemId) { |
| ITEMCLASS item = getUnfilteredItem(itemId); |
| if (getFilters().isEmpty()) { |
| return true; |
| } |
| final Iterator<Filter> i = getFilters().iterator(); |
| while (i.hasNext()) { |
| final Filter f = i.next(); |
| if (!f.passesFilter(itemId, item)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Adds a container filter and re-filter the view. |
| * |
| * The filter must implement Filter and its sub-filters (if any) must also |
| * be in-memory filterable. |
| * |
| * This can be used to implement |
| * {@link Filterable#addContainerFilter(com.vaadin.data.Container.Filter)} |
| * and optionally also |
| * {@link SimpleFilterable#addContainerFilter(Object, String, boolean, boolean)} |
| * (with {@link SimpleStringFilter}). |
| * |
| * Note that in some cases, incompatible filters cannot be detected when |
| * added and an {@link UnsupportedFilterException} may occur when performing |
| * filtering. |
| * |
| * @throws UnsupportedFilterException |
| * if the filter is detected as not supported by the container |
| */ |
| protected void addFilter(Filter filter) throws UnsupportedFilterException { |
| getFilters().add(filter); |
| filterAll(); |
| } |
| |
| /** |
| * Returns true if any filters have been applied to the container. |
| * |
| * @return true if the container has filters applied, false otherwise |
| * @since 7.1 |
| */ |
| protected boolean hasContainerFilters() { |
| return !getContainerFilters().isEmpty(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see com.vaadin.data.Container.Filterable#getContainerFilters() |
| */ |
| protected Collection<Filter> getContainerFilters() { |
| return Collections.unmodifiableCollection(filters); |
| } |
| |
| /** |
| * Remove a specific container filter and re-filter the view (if necessary). |
| * |
| * This can be used to implement |
| * {@link Filterable#removeContainerFilter(com.vaadin.data.Container.Filter)} |
| * . |
| */ |
| protected void removeFilter(Filter filter) { |
| for (Iterator<Filter> iterator = getFilters().iterator(); iterator |
| .hasNext();) { |
| Filter f = iterator.next(); |
| if (f.equals(filter)) { |
| iterator.remove(); |
| filterAll(); |
| return; |
| } |
| } |
| } |
| |
| /** |
| * Remove all container filters for all properties and re-filter the view. |
| * |
| * This can be used to implement |
| * {@link Filterable#removeAllContainerFilters()}. |
| */ |
| protected void removeAllFilters() { |
| if (getFilters().isEmpty()) { |
| return; |
| } |
| getFilters().clear(); |
| filterAll(); |
| } |
| |
| /** |
| * Checks if there is a filter that applies to a given property. |
| * |
| * @param propertyId |
| * @return true if there is an active filter for the property |
| */ |
| protected boolean isPropertyFiltered(Object propertyId) { |
| if (getFilters().isEmpty() || propertyId == null) { |
| return false; |
| } |
| final Iterator<Filter> i = getFilters().iterator(); |
| while (i.hasNext()) { |
| final Filter f = i.next(); |
| if (f.appliesToProperty(propertyId)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Remove all container filters for a given property identifier and |
| * re-filter the view. This also removes filters applying to multiple |
| * properties including the one identified by propertyId. |
| * |
| * This can be used to implement |
| * {@link Filterable#removeContainerFilters(Object)}. |
| * |
| * @param propertyId |
| * @return Collection<Filter> removed filters |
| */ |
| protected Collection<Filter> removeFilters(Object propertyId) { |
| if (getFilters().isEmpty() || propertyId == null) { |
| return Collections.emptyList(); |
| } |
| List<Filter> removedFilters = new LinkedList<Filter>(); |
| for (Iterator<Filter> iterator = getFilters().iterator(); iterator |
| .hasNext();) { |
| Filter f = iterator.next(); |
| if (f.appliesToProperty(propertyId)) { |
| removedFilters.add(f); |
| iterator.remove(); |
| } |
| } |
| if (!removedFilters.isEmpty()) { |
| filterAll(); |
| return removedFilters; |
| } |
| return Collections.emptyList(); |
| } |
| |
| // sorting |
| |
| /** |
| * Returns the ItemSorter used for comparing items in a sort. See |
| * {@link #setItemSorter(ItemSorter)} for more information. |
| * |
| * @return The ItemSorter used for comparing two items in a sort. |
| */ |
| protected ItemSorter getItemSorter() { |
| return itemSorter; |
| } |
| |
| /** |
| * Sets the ItemSorter used for comparing items in a sort. The |
| * {@link ItemSorter#compare(Object, Object)} method is called with item ids |
| * to perform the sorting. A default ItemSorter is used if this is not |
| * explicitly set. |
| * |
| * @param itemSorter |
| * The ItemSorter used for comparing two items in a sort (not |
| * null). |
| */ |
| protected void setItemSorter(ItemSorter itemSorter) { |
| this.itemSorter = itemSorter; |
| } |
| |
| /** |
| * Sort base implementation to be used to implement {@link Sortable}. |
| * |
| * Subclasses should call this from a public |
| * {@link #sort(Object[], boolean[])} method when implementing Sortable. |
| * |
| * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[], |
| * boolean[]) |
| */ |
| protected void sortContainer(Object[] propertyId, boolean[] ascending) { |
| if (!(this instanceof Sortable)) { |
| throw new UnsupportedOperationException( |
| "Cannot sort a Container that does not implement Sortable"); |
| } |
| |
| // Set up the item sorter for the sort operation |
| getItemSorter().setSortProperties((Sortable) this, propertyId, |
| ascending); |
| |
| // Perform the actual sort |
| doSort(); |
| |
| // Post sort updates |
| if (isFiltered()) { |
| filterAll(); |
| } else { |
| fireItemSetChange(); |
| } |
| |
| } |
| |
| /** |
| * Perform the sorting of the data structures in the container. This is |
| * invoked when the <code>itemSorter</code> has been prepared for the sort |
| * operation. Typically this method calls |
| * <code>Collections.sort(aCollection, getItemSorter())</code> on all arrays |
| * (containing item ids) that need to be sorted. |
| * |
| */ |
| protected void doSort() { |
| Collections.sort(getAllItemIds(), getItemSorter()); |
| } |
| |
| /** |
| * Returns the sortable property identifiers for the container. Can be used |
| * to implement {@link Sortable#getSortableContainerPropertyIds()}. |
| */ |
| protected Collection<?> getSortablePropertyIds() { |
| LinkedList<Object> sortables = new LinkedList<Object>(); |
| for (Object propertyId : getContainerPropertyIds()) { |
| Class<?> propertyType = getType(propertyId); |
| if (Comparable.class.isAssignableFrom(propertyType) |
| || propertyType.isPrimitive()) { |
| sortables.add(propertyId); |
| } |
| } |
| return sortables; |
| } |
| |
| // removing items |
| |
| /** |
| * Removes all items from the internal data structures of this class. This |
| * can be used to implement {@link #removeAllItems()} in subclasses. |
| * |
| * No notification is sent, the caller has to fire a suitable item set |
| * change notification. |
| */ |
| protected void internalRemoveAllItems() { |
| // Removes all Items |
| getAllItemIds().clear(); |
| if (isFiltered()) { |
| getFilteredItemIds().clear(); |
| } |
| } |
| |
| /** |
| * Removes a single item from the internal data structures of this class. |
| * This can be used to implement {@link #removeItem(Object)} in subclasses. |
| * |
| * No notification is sent, the caller has to fire a suitable item set |
| * change notification. |
| * |
| * @param itemId |
| * the identifier of the item to remove |
| * @return true if an item was successfully removed, false if failed to |
| * remove or no such item |
| */ |
| protected boolean internalRemoveItem(Object itemId) { |
| if (itemId == null) { |
| return false; |
| } |
| |
| boolean result = getAllItemIds().remove(itemId); |
| if (result && isFiltered()) { |
| getFilteredItemIds().remove(itemId); |
| } |
| |
| return result; |
| } |
| |
| // adding items |
| |
| /** |
| * Adds the bean to all internal data structures at the given position. |
| * Fails if an item with itemId is already in the container. Returns a the |
| * item if it was added successfully, null otherwise. |
| * |
| * <p> |
| * Caller should initiate filtering after calling this method. |
| * </p> |
| * |
| * For internal use only - subclasses should use |
| * {@link #internalAddItemAtEnd(Object, Item, boolean)}, |
| * {@link #internalAddItemAt(int, Object, Item, boolean)} and |
| * {@link #internalAddItemAfter(Object, Object, Item, boolean)} instead. |
| * |
| * @param position |
| * The position at which the item should be inserted in the |
| * unfiltered collection of items |
| * @param itemId |
| * The item identifier for the item to insert |
| * @param item |
| * The item to insert |
| * |
| * @return ITEMCLASS if the item was added successfully, null otherwise |
| */ |
| protected ITEMCLASS internalAddAt(int position, ITEMIDTYPE itemId, |
| ITEMCLASS item) { |
| if (position < 0 || position > getAllItemIds().size() || itemId == null |
| || item == null) { |
| return null; |
| } |
| // Make sure that the item has not been added previously |
| if (getAllItemIds().contains(itemId)) { |
| return null; |
| } |
| |
| // "filteredList" will be updated in filterAll() which should be invoked |
| // by the caller after calling this method. |
| getAllItemIds().add(position, itemId); |
| registerNewItem(position, itemId, item); |
| |
| return item; |
| } |
| |
| /** |
| * Add an item at the end of the container, and perform filtering if |
| * necessary. An event is fired if the filtered view changes. |
| * |
| * @param newItemId |
| * @param item |
| * new item to add |
| * @param filter |
| * true to perform filtering and send event after adding the |
| * item, false to skip these operations for batch inserts - if |
| * false, caller needs to make sure these operations are |
| * performed at the end of the batch |
| * @return item added or null if no item was added |
| */ |
| protected ITEMCLASS internalAddItemAtEnd(ITEMIDTYPE newItemId, |
| ITEMCLASS item, boolean filter) { |
| ITEMCLASS newItem = internalAddAt(getAllItemIds().size(), newItemId, |
| item); |
| if (newItem != null && filter) { |
| // TODO filter only this item, use fireItemAdded() |
| filterAll(); |
| if (!isFiltered()) { |
| // TODO hack: does not detect change in filterAll() in this case |
| fireItemAdded(indexOfId(newItemId), newItemId, item); |
| } |
| } |
| return newItem; |
| } |
| |
| /** |
| * Add an item after a given (visible) item, and perform filtering. An event |
| * is fired if the filtered view changes. |
| * |
| * The new item is added at the beginning if previousItemId is null. |
| * |
| * @param previousItemId |
| * item id of a visible item after which to add the new item, or |
| * null to add at the beginning |
| * @param newItemId |
| * @param item |
| * new item to add |
| * @param filter |
| * true to perform filtering and send event after adding the |
| * item, false to skip these operations for batch inserts - if |
| * false, caller needs to make sure these operations are |
| * performed at the end of the batch |
| * @return item added or null if no item was added |
| */ |
| protected ITEMCLASS internalAddItemAfter(ITEMIDTYPE previousItemId, |
| ITEMIDTYPE newItemId, ITEMCLASS item, boolean filter) { |
| // only add if the previous item is visible |
| ITEMCLASS newItem = null; |
| if (previousItemId == null) { |
| newItem = internalAddAt(0, newItemId, item); |
| } else if (containsId(previousItemId)) { |
| newItem = internalAddAt(getAllItemIds().indexOf(previousItemId) + 1, |
| newItemId, item); |
| } |
| if (newItem != null && filter) { |
| // TODO filter only this item, use fireItemAdded() |
| filterAll(); |
| if (!isFiltered()) { |
| // TODO hack: does not detect change in filterAll() in this case |
| fireItemAdded(indexOfId(newItemId), newItemId, item); |
| } |
| } |
| return newItem; |
| } |
| |
| /** |
| * Add an item at a given (visible after filtering) item index, and perform |
| * filtering. An event is fired if the filtered view changes. |
| * |
| * @param index |
| * position where to add the item (visible/view index) |
| * @param newItemId |
| * @param item |
| * new item to add |
| * @param filter |
| * true to perform filtering and send event after adding the |
| * item, false to skip these operations for batch inserts - if |
| * false, caller needs to make sure these operations are |
| * performed at the end of the batch |
| * @return item added or null if no item was added |
| */ |
| protected ITEMCLASS internalAddItemAt(int index, ITEMIDTYPE newItemId, |
| ITEMCLASS item, boolean filter) { |
| if (index < 0 || index > size()) { |
| return null; |
| } else if (index == 0) { |
| // add before any item, visible or not |
| return internalAddItemAfter(null, newItemId, item, filter); |
| } else { |
| // if index==size(), adds immediately after last visible item |
| return internalAddItemAfter(getIdByIndex(index - 1), newItemId, |
| item, filter); |
| } |
| } |
| |
| /** |
| * Registers a new item as having been added to the container. This can |
| * involve storing the item or any relevant information about it in internal |
| * container-specific collections if necessary, as well as registering |
| * listeners etc. |
| * |
| * The full identifier list in {@link AbstractInMemoryContainer} has already |
| * been updated to reflect the new item when this method is called. |
| * |
| * @param position |
| * @param itemId |
| * @param item |
| */ |
| protected void registerNewItem(int position, ITEMIDTYPE itemId, |
| ITEMCLASS item) { |
| } |
| |
| // item set change notifications |
| |
| /** |
| * Notify item set change listeners that an item has been added to the |
| * container. |
| * |
| * @since 7.4 |
| * |
| * @param position |
| * 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) { |
| 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. |
| * |
| * @since 7.4 |
| * |
| * @param position |
| * 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) { |
| 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 |
| |
| /** |
| * Returns the internal list of visible item identifiers after filtering. |
| * |
| * For internal use only. |
| */ |
| protected List<ITEMIDTYPE> getVisibleItemIds() { |
| if (isFiltered()) { |
| return getFilteredItemIds(); |
| } else { |
| return getAllItemIds(); |
| } |
| } |
| |
| /** |
| * Returns the item id of the first visible item after filtering. 'Null' is |
| * returned if there is no visible items. |
| * <p> |
| * For internal use only. |
| * |
| * @since 7.4 |
| * |
| * @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. |
| * |
| * @return true if the container is currently filtered |
| */ |
| protected boolean isFiltered() { |
| return filteredItemIds != null; |
| } |
| |
| /** |
| * Internal helper method to set the internal list of filtered item |
| * identifiers. Should not be used outside this class except for |
| * implementing clone(), may disappear from future versions. |
| * |
| * @param filteredItemIds |
| */ |
| @Deprecated |
| protected void setFilteredItemIds(List<ITEMIDTYPE> filteredItemIds) { |
| this.filteredItemIds = filteredItemIds; |
| } |
| |
| /** |
| * Internal helper method to get the internal list of filtered item |
| * identifiers. Should not be used outside this class except for |
| * implementing clone(), may disappear from future versions - use |
| * {@link #getVisibleItemIds()} in other contexts. |
| * |
| * @return List<ITEMIDTYPE> |
| */ |
| protected List<ITEMIDTYPE> getFilteredItemIds() { |
| return filteredItemIds; |
| } |
| |
| /** |
| * Internal helper method to set the internal list of all item identifiers. |
| * Should not be used outside this class except for implementing clone(), |
| * may disappear from future versions. |
| * |
| * @param allItemIds |
| */ |
| @Deprecated |
| protected void setAllItemIds(List<ITEMIDTYPE> allItemIds) { |
| this.allItemIds = allItemIds; |
| } |
| |
| /** |
| * Internal helper method to get the internal list of all item identifiers. |
| * Avoid using this method outside this class, may disappear in future |
| * versions. |
| * |
| * @return List<ITEMIDTYPE> |
| */ |
| protected List<ITEMIDTYPE> getAllItemIds() { |
| return allItemIds; |
| } |
| |
| /** |
| * Set the internal collection of filters without performing filtering. |
| * |
| * This method is mostly for internal use, use |
| * {@link #addFilter(com.vaadin.data.Container.Filter)} and |
| * <code>remove*Filter*</code> (which also re-filter the container) instead |
| * when possible. |
| * |
| * @param filters |
| */ |
| protected void setFilters(Set<Filter> filters) { |
| this.filters = filters; |
| } |
| |
| /** |
| * Returns the internal collection of filters. The returned collection |
| * should not be modified by callers outside this class. |
| * |
| * @return Set<Filter> |
| */ |
| protected Set<Filter> getFilters() { |
| return filters; |
| } |
| |
| } |