blob: 26e8473424a8b09e119dcf31e56031134b3adf57 [file] [log] [blame]
/*
* 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;
}
}