aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/vaadin/data/util
diff options
context:
space:
mode:
authorMatti Tahvonen <matti.tahvonen@itmill.com>2010-03-05 13:43:43 +0000
committerMatti Tahvonen <matti.tahvonen@itmill.com>2010-03-05 13:43:43 +0000
commit7cefe21c703e6e7822ba7456d45fa49a14dd067d (patch)
tree8891e28e97bce4f9db44dd7f50cbb6a844de8aef /src/com/vaadin/data/util
parent9b84a02d3f46d1f4dca4cc3b17c388d51ca5194a (diff)
parent2b1deeeced6d098a22d6e52b70e94dbb9e96df57 (diff)
downloadvaadin-framework-7cefe21c703e6e7822ba7456d45fa49a14dd067d.tar.gz
vaadin-framework-7cefe21c703e6e7822ba7456d45fa49a14dd067d.zip
merged changes from 6.3 + some container related changes
svn changeset:11664/svn branch:6.3_dd
Diffstat (limited to 'src/com/vaadin/data/util')
-rw-r--r--src/com/vaadin/data/util/BeanItemContainer.java127
-rw-r--r--src/com/vaadin/data/util/ContainerHierarchicalWrapper.java13
-rw-r--r--src/com/vaadin/data/util/HierarchicalContainer.java312
-rw-r--r--src/com/vaadin/data/util/IndexedContainer.java29
-rw-r--r--src/com/vaadin/data/util/ListSet.java198
5 files changed, 593 insertions, 86 deletions
diff --git a/src/com/vaadin/data/util/BeanItemContainer.java b/src/com/vaadin/data/util/BeanItemContainer.java
index 7848de5eed..6510229d5a 100644
--- a/src/com/vaadin/data/util/BeanItemContainer.java
+++ b/src/com/vaadin/data/util/BeanItemContainer.java
@@ -38,9 +38,23 @@ import com.vaadin.data.Property.ValueChangeNotifier;
@SuppressWarnings("serial")
public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
ItemSetChangeNotifier, ValueChangeListener {
- // filtered and unfiltered item IDs
- private ArrayList<BT> list = new ArrayList<BT>();
- private ArrayList<BT> allItems = new ArrayList<BT>();
+ /**
+ * The filteredItems variable contains the items that are visible outside
+ * the container. If filters are enabled this contains a subset of allItems,
+ * if no filters are set this contains the same items as allItems.
+ */
+ private ListSet<BT> filteredItems = new ListSet<BT>();
+
+ /**
+ * The allItems variable always contains all the items in the container.
+ * Some or all of these are also in the filteredItems list.
+ */
+ private ListSet<BT> allItems = new ListSet<BT>();
+
+ /**
+ * Maps all pojos (item ids) in the container (including filtered) to their
+ * corresponding BeanItem.
+ */
private final Map<BT, BeanItem<BT>> beanToItem = new HashMap<BT, BeanItem<BT>>();
// internal data model to obtain property IDs etc.
@@ -89,6 +103,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
* @throws IllegalArgumentException
* If the collection is null or empty.
*/
+ @SuppressWarnings("unchecked")
public BeanItemContainer(Collection<BT> collection)
throws IllegalArgumentException {
if (collection == null || collection.isEmpty()) {
@@ -98,10 +113,22 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
type = (Class<? extends BT>) collection.iterator().next().getClass();
model = BeanItem.getPropertyDescriptors(type);
- int i = 0;
- for (BT bt : collection) {
- addItemAt(i++, bt);
+ addAll(collection);
+ }
+
+ private void addAll(Collection<BT> collection) {
+ // Pre-allocate space for the collection
+ allItems.ensureCapacity(allItems.size() + collection.size());
+
+ int idx = size();
+ for (BT bean : collection) {
+ if (internalAddAt(idx, bean) != null) {
+ idx++;
+ }
}
+
+ // Filter the contents when all items have been added
+ filterAll();
}
/**
@@ -147,36 +174,65 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
* @return Returns new item or null if the operation fails.
*/
private BeanItem<BT> addItemAtInternalIndex(int index, Object newItemId) {
- // Make sure that the Item has not been created yet
- if (allItems.contains(newItemId)) {
+ BeanItem<BT> beanItem = internalAddAt(index, (BT) newItemId);
+ if (beanItem != null) {
+ filterAll();
+ }
+
+ return beanItem;
+ }
+
+ /**
+ * Adds the bean to all internal data structures at the given position.
+ * Fails if the bean is already in the container or is not assignable to the
+ * correct type. Returns the new BeanItem if the bean was added
+ * successfully.
+ *
+ * <p>
+ * Caller should call {@link #filterAll()} after calling this method to
+ * ensure the filtered list is updated.
+ * </p>
+ *
+ * @param position
+ * The position at which the bean should be inserted
+ * @param bean
+ * The bean to insert
+ *
+ * @return true if the bean was added successfully, false otherwise
+ */
+ private BeanItem<BT> internalAddAt(int position, BT bean) {
+ // Make sure that the item has not been added previously
+ if (allItems.contains(bean)) {
return null;
}
- if (type.isAssignableFrom(newItemId.getClass())) {
- BT pojo = (BT) newItemId;
- // "list" will be updated in filterAll()
- allItems.add(index, pojo);
- BeanItem<BT> beanItem = new BeanItem<BT>(pojo, model);
- beanToItem.put(pojo, beanItem);
- // add listeners to be able to update filtering on property changes
- for (Filter filter : filters) {
- // addValueChangeListener avoids adding duplicates
- addValueChangeListener(beanItem, filter.propertyId);
- }
- // it is somewhat suboptimal to filter all items
- filterAll();
- return beanItem;
- } else {
+ if (!type.isAssignableFrom(bean.getClass())) {
return null;
}
+
+ // "filteredList" will be updated in filterAll() which should be invoked
+ // by the caller after calling this method.
+ allItems.add(position, bean);
+ BeanItem<BT> beanItem = new BeanItem<BT>(bean, model);
+ beanToItem.put(bean, beanItem);
+
+ // add listeners to be able to update filtering on property
+ // changes
+ for (Filter filter : filters) {
+ // addValueChangeListener avoids adding duplicates
+ addValueChangeListener(beanItem, filter.propertyId);
+ }
+
+ return beanItem;
}
+ @SuppressWarnings("unchecked")
public BT getIdByIndex(int index) {
- return list.get(index);
+ return filteredItems.get(index);
}
public int indexOfId(Object itemId) {
- return list.indexOf(itemId);
+ return filteredItems.indexOf(itemId);
}
/**
@@ -296,7 +352,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
public boolean containsId(Object itemId) {
// only look at visible items after filtering
- return list.contains(itemId);
+ return filteredItems.contains(itemId);
}
public Property getContainerProperty(Object itemId, Object propertyId) {
@@ -311,8 +367,9 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
return beanToItem.get(itemId);
}
+ @SuppressWarnings("unchecked")
public Collection<BT> getItemIds() {
- return (Collection<BT>) list.clone();
+ return (Collection<BT>) filteredItems.clone();
}
public Class<?> getType(Object propertyId) {
@@ -321,7 +378,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
public boolean removeAllItems() throws UnsupportedOperationException {
allItems.clear();
- list.clear();
+ filteredItems.clear();
// detach listeners from all BeanItems
for (BeanItem<BT> item : beanToItem.values()) {
removeAllValueChangeListeners(item);
@@ -345,7 +402,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
removeAllValueChangeListeners(getItem(itemId));
// remove item
beanToItem.remove(itemId);
- list.remove(itemId);
+ filteredItems.remove(itemId);
fireItemSetChange();
return true;
}
@@ -375,7 +432,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
}
public int size() {
- return list.size();
+ return filteredItems.size();
}
public Collection<Object> getSortableContainerPropertyIds() {
@@ -445,7 +502,7 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
public void addContainerFilter(Object propertyId, String filterString,
boolean ignoreCase, boolean onlyMatchPrefix) {
if (filters.isEmpty()) {
- list = (ArrayList<BT>) allItems.clone();
+ filteredItems = (ListSet<BT>) allItems.clone();
}
// listen to change events to be able to update filtering
for (BeanItem<BT> item : beanToItem.values()) {
@@ -465,22 +522,22 @@ public class BeanItemContainer<BT> implements Indexed, Sortable, Filterable,
*/
protected void filterAll() {
// avoid notification if the filtering had no effect
- List<BT> originalItems = list;
+ List<BT> originalItems = filteredItems;
// it is somewhat inefficient to do a (shallow) clone() every time
- list = (ArrayList<BT>) allItems.clone();
+ filteredItems = (ListSet<BT>) allItems.clone();
for (Filter f : filters) {
filter(f);
}
// check if exactly the same items are there after filtering to avoid
// unnecessary notifications
// this may be slow in some cases as it uses BT.equals()
- if (!originalItems.equals(list)) {
+ if (!originalItems.equals(filteredItems)) {
fireItemSetChange();
}
}
protected void filter(Filter f) {
- Iterator<BT> iterator = list.iterator();
+ Iterator<BT> iterator = filteredItems.iterator();
while (iterator.hasNext()) {
BT bean = iterator.next();
if (!f.passesFilter(getItem(bean))) {
diff --git a/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java b/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java
index 80065f9254..8b6fedb8e4 100644
--- a/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java
+++ b/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java
@@ -220,7 +220,12 @@ public class ContainerHierarchicalWrapper implements Container.Hierarchical,
return ((Container.Hierarchical) container)
.areChildrenAllowed(itemId);
}
- return !noChildrenAllowed.contains(itemId);
+
+ if (noChildrenAllowed.contains(itemId)) {
+ return false;
+ }
+
+ return containsId(itemId);
}
/*
@@ -284,7 +289,11 @@ public class ContainerHierarchicalWrapper implements Container.Hierarchical,
return ((Container.Hierarchical) container).isRoot(itemId);
}
- return parent.get(itemId) == null;
+ if (parent.containsKey(itemId)) {
+ return false;
+ }
+
+ return containsId(itemId);
}
/*
diff --git a/src/com/vaadin/data/util/HierarchicalContainer.java b/src/com/vaadin/data/util/HierarchicalContainer.java
index 2b5519e651..a404d83eb1 100644
--- a/src/com/vaadin/data/util/HierarchicalContainer.java
+++ b/src/com/vaadin/data/util/HierarchicalContainer.java
@@ -42,16 +42,29 @@ public class HierarchicalContainer extends IndexedContainer implements
private final HashMap<Object, LinkedList<Object>> children = new HashMap<Object, LinkedList<Object>>();
/**
+ * Mapping from Item ID to a list of child IDs when filtered
+ */
+ private HashMap<Object, LinkedList<Object>> filteredChildren = null;
+
+ /**
* List that contains all root elements of the container.
*/
private final LinkedList<Object> roots = new LinkedList<Object>();
+ /**
+ * List that contains all filtered root elements of the container.
+ */
+ private LinkedList<Object> filteredRoots = null;
+
/*
* Can the specified Item have any children? Don't add a JavaDoc comment
* here, we use the default documentation from implemented interface.
*/
public boolean areChildrenAllowed(Object itemId) {
- return !noChildrenAllowed.contains(itemId);
+ if (noChildrenAllowed.contains(itemId)) {
+ return false;
+ }
+ return containsId(itemId);
}
/*
@@ -60,7 +73,14 @@ public class HierarchicalContainer extends IndexedContainer implements
* interface.
*/
public Collection getChildren(Object itemId) {
- final Collection c = children.get(itemId);
+ LinkedList<Object> c;
+
+ if (filteredChildren != null) {
+ c = filteredChildren.get(itemId);
+ } else {
+ c = children.get(itemId);
+ }
+
if (c == null) {
return null;
}
@@ -82,7 +102,11 @@ public class HierarchicalContainer extends IndexedContainer implements
* interface.
*/
public boolean hasChildren(Object itemId) {
- return children.get(itemId) != null;
+ if (filteredChildren != null) {
+ return filteredChildren.containsKey(itemId);
+ } else {
+ return children.containsKey(itemId);
+ }
}
/*
@@ -91,7 +115,15 @@ public class HierarchicalContainer extends IndexedContainer implements
* interface.
*/
public boolean isRoot(Object itemId) {
- return parent.get(itemId) == null;
+ if (filteredRoots != null && !filteredRoots.contains(itemId)) {
+ return false;
+ }
+
+ if (parent.containsKey(itemId)) {
+ return false;
+ }
+
+ return containsId(itemId);
}
/*
@@ -100,7 +132,11 @@ public class HierarchicalContainer extends IndexedContainer implements
* interface.
*/
public Collection rootItemIds() {
- return Collections.unmodifiableCollection(roots);
+ if (filteredRoots != null) {
+ return Collections.unmodifiableCollection(filteredRoots);
+ } else {
+ return Collections.unmodifiableCollection(roots);
+ }
}
/**
@@ -139,25 +175,6 @@ public class HierarchicalContainer extends IndexedContainer implements
return true;
}
- @Override
- public Item addItemAt(int index, Object newItemId) {
- Item retval = super.addItemAt(index, newItemId);
- if (getParent(newItemId) == null) {
- int refIndex = roots.size() - 1;
- int indexOfId = indexOfId(roots.get(refIndex));
- while (indexOfId > index) {
- refIndex--;
- if (refIndex < 0) {
- // inserts as first
- break;
- }
- indexOfId = indexOfId(roots.get(refIndex));
- }
- roots.add(refIndex + 1, newItemId);
- }
- return retval;
- }
-
/**
* <p>
* Sets the parent of an Item. The new parent item must exist and be able to
@@ -191,27 +208,58 @@ public class HierarchicalContainer extends IndexedContainer implements
return true;
}
- // Making root
+ // Making root?
if (newParentId == null) {
+ // The itemId should become a root so we need to
+ // - Remove it from the old parent's children list (also filtered
+ // list)
+ // - Add it as a root
+ // - Remove it from the item -> parent list (parent is null for
+ // roots)
// Removes from old parents children list
- final LinkedList l = children.get(itemId);
+ final LinkedList<Object> l = children.get(itemId);
if (l != null) {
l.remove(itemId);
if (l.isEmpty()) {
children.remove(itemId);
}
+
+ if (filteredChildren != null) {
+ LinkedList<Object> f = filteredChildren.get(itemId);
+ if (f != null) {
+ f.remove(itemId);
+ if (f.isEmpty()) {
+ filteredChildren.remove(f);
+ }
+ }
+ }
}
// Add to be a root
roots.add(itemId);
+ if (filteredRoots != null) {
+ if (passesFilters(itemId)) {
+ filteredRoots.add(itemId);
+ }
+ }
// Updates parent
parent.remove(itemId);
+ fireContentsChange(-1);
+
return true;
}
+ // We get here when the item should not become a root and we need to
+ // - Verify the new parent exists and can have children
+ // - Check that the new parent is not a child of the selected itemId
+ // - Updated the item -> parent mapping to point to the new parent
+ // - Remove the item from the roots list if it was a root
+ // - Remove the item from the old parent's children list if it was not a
+ // root
+
// Checks that the new parent exists in container and can have
// children
if (!containsId(newParentId) || noChildrenAllowed.contains(newParentId)) {
@@ -229,29 +277,92 @@ public class HierarchicalContainer extends IndexedContainer implements
// Updates parent
parent.put(itemId, newParentId);
- LinkedList pcl = children.get(newParentId);
+ LinkedList<Object> pcl = children.get(newParentId);
if (pcl == null) {
- pcl = new LinkedList();
+ // Create an empty list for holding children if one were not
+ // previously created
+ pcl = new LinkedList<Object>();
children.put(newParentId, pcl);
}
pcl.add(itemId);
+ // Add children list for filtered case also
+ if (filteredChildren != null) {
+ LinkedList<Object> f = filteredChildren.get(newParentId);
+ if (f == null) {
+ // Create an empty list for holding children if one were not
+ // previously created
+ f = new LinkedList<Object>();
+ filteredChildren.put(newParentId, f);
+ }
+ }
+
// Removes from old parent or root
if (oldParentId == null) {
roots.remove(itemId);
} else {
- final LinkedList l = children.get(oldParentId);
+ final LinkedList<Object> l = children.get(oldParentId);
if (l != null) {
l.remove(itemId);
if (l.isEmpty()) {
children.remove(oldParentId);
}
}
+ if (filteredChildren != null) {
+ LinkedList<Object> f = filteredChildren.get(oldParentId);
+ if (f != null) {
+ f.remove(itemId);
+ if (f.isEmpty()) {
+ filteredChildren.remove(oldParentId);
+ }
+ }
+ }
}
+ fireContentsChange(-1);
+
return true;
}
+ /**
+ * TODO javadoc
+ *
+ * @param itemId
+ * @param siblingId
+ */
+ public void moveAfterSibling(Object itemId, Object siblingId) {
+ Object parent2 = getParent(itemId);
+ LinkedList<Object> childrenList;
+ if (parent2 == null) {
+ childrenList = roots;
+ } else {
+ childrenList = children.get(parent2);
+ }
+ if (siblingId == null) {
+ childrenList.remove(itemId);
+ childrenList.addFirst(itemId);
+
+ } else {
+ int oldIndex = childrenList.indexOf(itemId);
+ int indexOfSibling = childrenList.indexOf(siblingId);
+ if (indexOfSibling != -1 && oldIndex != -1) {
+ int newIndex;
+ if (oldIndex > indexOfSibling) {
+ newIndex = indexOfSibling + 1;
+ } else {
+ newIndex = indexOfSibling;
+ }
+ childrenList.remove(oldIndex);
+ childrenList.add(newIndex, itemId);
+ } else {
+ throw new IllegalArgumentException(
+ "Given identifiers no not have the same parent.");
+ }
+ }
+ fireContentsChange(-1);
+
+ }
+
/*
* (non-Javadoc)
*
@@ -259,12 +370,21 @@ public class HierarchicalContainer extends IndexedContainer implements
*/
@Override
public Object addItem() {
- final Object id = super.addItem();
- if (id != null && !roots.contains(id)) {
- roots.add(id);
+ final Object itemId = super.addItem();
+ if (itemId == null) {
+ return null;
}
- return id;
+ if (!roots.contains(itemId)) {
+ roots.add(itemId);
+ if (filteredRoots != null) {
+ if (passesFilters(itemId)) {
+ filteredRoots.add(itemId);
+ }
+ }
+ }
+
+ return itemId;
}
/*
@@ -275,9 +395,18 @@ public class HierarchicalContainer extends IndexedContainer implements
@Override
public Item addItem(Object itemId) {
final Item item = super.addItem(itemId);
- if (item != null) {
- roots.add(itemId);
+ if (item == null) {
+ return null;
+ }
+
+ roots.add(itemId);
+
+ if (filteredRoots != null) {
+ if (passesFilters(itemId)) {
+ filteredRoots.add(itemId);
+ }
}
+
return item;
}
@@ -295,6 +424,12 @@ public class HierarchicalContainer extends IndexedContainer implements
parent.clear();
children.clear();
noChildrenAllowed.clear();
+ if (filteredRoots != null) {
+ filteredRoots = null;
+ }
+ if (filteredChildren != null) {
+ filteredChildren = null;
+ }
}
return success;
}
@@ -309,20 +444,44 @@ public class HierarchicalContainer extends IndexedContainer implements
final boolean success = super.removeItem(itemId);
if (success) {
- if (isRoot(itemId)) {
- roots.remove(itemId);
+ // Remove from roots if this was a root
+ if (roots.remove(itemId)) {
+
+ // If filtering is enabled we might need to remove it from the
+ // filtered list also
+ if (filteredRoots != null) {
+ filteredRoots.remove(itemId);
+ }
}
- LinkedList<Object> remove = children.remove(itemId);
- if (remove != null) {
- for (Object object : remove) {
- removeItem(object);
+
+ // Clear the children list. Old children will now become root nodes
+ LinkedList<Object> childNodeIds = children.remove(itemId);
+ if (childNodeIds != null) {
+ if (filteredChildren != null) {
+ filteredChildren.remove(itemId);
+ }
+ for (Object childId : childNodeIds) {
+ setParent(childId, null);
}
}
- final Object p = parent.get(itemId);
- if (p != null) {
- final LinkedList c = children.get(p);
+
+ // Parent of the item that we are removing will contain the item id
+ // in its children list
+ final Object parentItemId = parent.get(itemId);
+ if (parentItemId != null) {
+ final LinkedList<Object> c = children.get(parentItemId);
if (c != null) {
c.remove(itemId);
+
+ // Found in the children list so might also be in the
+ // filteredChildren list
+ if (filteredChildren != null) {
+ LinkedList<Object> f = filteredChildren
+ .get(parentItemId);
+ if (f != null) {
+ f.remove(parentItemId);
+ }
+ }
}
}
parent.remove(itemId);
@@ -332,6 +491,35 @@ public class HierarchicalContainer extends IndexedContainer implements
return success;
}
+ /**
+ * Removes the Item identified by ItemId from the Container and all its
+ * children.
+ *
+ * @see #removeItem(Object)
+ * @param itemId
+ * the identifier of the Item to remove
+ * @return true if the operation succeeded
+ */
+ public boolean removeItemRecursively(Object itemId) {
+ boolean success = true;
+ Collection<Object> children2 = getChildren(itemId);
+ if (children2 != null) {
+ Object[] array = children2.toArray();
+ for (int i = 0; i < array.length; i++) {
+ boolean removeItemRecursively = removeItemRecursively(array[i]);
+ if (!removeItemRecursively) {
+ success = false;
+ }
+ }
+ }
+ boolean removeItem = removeItem(itemId);
+ if (!removeItem) {
+ success = false;
+ }
+ return success;
+
+ }
+
/*
* (non-Javadoc)
*
@@ -347,4 +535,40 @@ public class HierarchicalContainer extends IndexedContainer implements
}
}
+ /*
+ * Overridden to provide filtering for root & children items.
+ *
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.IndexedContainer#updateContainerFiltering()
+ */
+ @Override
+ protected void updateContainerFiltering() {
+ super.updateContainerFiltering();
+
+ filteredRoots = new LinkedList<Object>();
+ filteredChildren = new HashMap<Object, LinkedList<Object>>();
+
+ // Filter root item ids
+ for (Object rootId : roots) {
+ if (passesFilters(rootId)) {
+ filteredRoots.add(rootId);
+ }
+ }
+
+ // Filter children
+ for (Object parent : children.keySet()) {
+ if (passesFilters(parent)) {
+ LinkedList<Object> filtered = new LinkedList<Object>();
+ filteredChildren.put(parent, filtered);
+ for (Object child : children.get(parent)) {
+ if (passesFilters(child)) {
+ filtered.add(child);
+ }
+ }
+ }
+ }
+
+ }
+
}
diff --git a/src/com/vaadin/data/util/IndexedContainer.java b/src/com/vaadin/data/util/IndexedContainer.java
index d6d6d9e77c..3bac19cd7a 100644
--- a/src/com/vaadin/data/util/IndexedContainer.java
+++ b/src/com/vaadin/data/util/IndexedContainer.java
@@ -469,7 +469,13 @@ public class IndexedContainer implements Container.Indexed,
return null;
}
try {
- return itemIds.get(itemIds.indexOf(itemId) + 1);
+ int idx = itemIds.indexOf(itemId);
+ if (idx == -1) {
+ // If the given Item is not found in the Container,
+ // null is returned.
+ return null;
+ }
+ return itemIds.get(idx + 1);
} catch (final IndexOutOfBoundsException e) {
return null;
}
@@ -548,6 +554,11 @@ public class IndexedContainer implements Container.Indexed,
* java.lang.Object)
*/
public Item addItemAfter(Object previousItemId, Object newItemId) {
+ // Adding an item after null item adds the item as first item of the
+ // ordered container.
+ if (previousItemId == null) {
+ return addItemAt(0, newItemId);
+ }
// Get the index of the addition
int index = -1;
@@ -574,7 +585,11 @@ public class IndexedContainer implements Container.Indexed,
// Creates a new id
final Object id = generateId();
- return addItemAfter(previousItemId, id);
+ if (addItemAfter(previousItemId, id) != null) {
+ return id;
+ } else {
+ return null;
+ }
}
/*
@@ -953,7 +968,7 @@ public class IndexedContainer implements Container.Indexed,
* @param addedItemIndex
* index of new item if change event was an item addition
*/
- private void fireContentsChange(int addedItemIndex) {
+ protected void fireContentsChange(int addedItemIndex) {
if (itemSetChangeListeners != null) {
final Object[] l = itemSetChangeListeners.toArray();
final Container.ItemSetChangeEvent event = new IndexedContainer.ItemSetChangeEvent(
@@ -1549,7 +1564,7 @@ public class IndexedContainer implements Container.Indexed,
}
}
- private void updateContainerFiltering() {
+ protected void updateContainerFiltering() {
// Clearing filters?
if (filters == null || filters.isEmpty()) {
@@ -1571,7 +1586,7 @@ public class IndexedContainer implements Container.Indexed,
// Filter
for (final Iterator i = itemIds.iterator(); i.hasNext();) {
final Object id = i.next();
- if (passesFilters(new IndexedContainerItem(id))) {
+ if (passesFilters(id)) {
filteredItemIds.add(id);
}
}
@@ -1579,6 +1594,10 @@ public class IndexedContainer implements Container.Indexed,
fireContentsChange(-1);
}
+ protected final boolean passesFilters(Object itemId) {
+ return passesFilters(new IndexedContainerItem(itemId));
+ }
+
private boolean passesFilters(Item item) {
if (filters == null) {
return true;
diff --git a/src/com/vaadin/data/util/ListSet.java b/src/com/vaadin/data/util/ListSet.java
new file mode 100644
index 0000000000..8dc8c6bbfd
--- /dev/null
+++ b/src/com/vaadin/data/util/ListSet.java
@@ -0,0 +1,198 @@
+package com.vaadin.data.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * ListSet is an internal Vaadin class which implements a combination of a List
+ * and a Set. The main purpose of this class is to provide a fast
+ * {@link #contains(Object)} method. Each inserted object must by unique (as
+ * specified by {@link #equals(Object)}).
+ *
+ * This class is subject to change and should not be used outside Vaadin core.
+ */
+public class ListSet<E> extends ArrayList<E> {
+ private HashSet<E> itemSet = null;
+
+ public ListSet() {
+ super();
+ itemSet = new HashSet<E>();
+ }
+
+ public ListSet(Collection<? extends E> c) {
+ super(c);
+ itemSet = new HashSet<E>(c.size());
+ itemSet.addAll(c);
+ }
+
+ public ListSet(int initialCapacity) {
+ super(initialCapacity);
+ itemSet = new HashSet<E>(initialCapacity);
+ }
+
+ // Delegate contains operations to the set
+ @Override
+ public boolean contains(Object o) {
+ return itemSet.contains(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return itemSet.containsAll(c);
+ }
+
+ // Methods for updating the set when the list is updated.
+ @Override
+ public boolean add(E e) {
+ if (contains(e)) {
+ // Duplicates are not allowed
+ return false;
+ }
+
+ if (super.add(e)) {
+ itemSet.add(e);
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ /**
+ * Works as java.util.ArrayList#add(int, java.lang.Object) but returns
+ * immediately if the element is already in the ListSet.
+ */
+ @Override
+ public void add(int index, E element) {
+ if (contains(element)) {
+ // Duplicates are not allowed
+ return;
+ }
+
+ super.add(index, element);
+ itemSet.add(element);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> c) {
+ boolean modified = false;
+ Iterator<? extends E> i = c.iterator();
+ while (i.hasNext()) {
+ E e = i.next();
+ if (contains(e)) {
+ continue;
+ }
+
+ if (add(e)) {
+ itemSet.add(e);
+ modified = true;
+ }
+ }
+ return modified;
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends E> c) {
+ ensureCapacity(size() + c.size());
+
+ boolean modified = false;
+ Iterator<? extends E> i = c.iterator();
+ while (i.hasNext()) {
+ E e = i.next();
+ if (contains(e)) {
+ continue;
+ }
+
+ add(index++, e);
+ itemSet.add(e);
+ modified = true;
+ }
+
+ return modified;
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ itemSet.clear();
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ if (!contains(o)) {
+ return -1;
+ }
+
+ return super.indexOf(o);
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ if (!contains(o)) {
+ return -1;
+ }
+
+ return super.lastIndexOf(o);
+ }
+
+ @Override
+ public E remove(int index) {
+ E e = super.remove(index);
+
+ if (e != null) {
+ itemSet.remove(e);
+ }
+
+ return e;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (super.remove(o)) {
+ itemSet.remove(o);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void removeRange(int fromIndex, int toIndex) {
+ HashSet<E> toRemove = new HashSet<E>();
+ for (int idx = fromIndex; idx < toIndex; idx++) {
+ toRemove.add(get(idx));
+ }
+ super.removeRange(fromIndex, toIndex);
+ itemSet.removeAll(toRemove);
+ }
+
+ @Override
+ public E set(int index, E element) {
+ if (contains(element)) {
+ // Element already exist in the list
+ if (get(index) == element) {
+ // At the same position, nothing to be done
+ return element;
+ } else {
+ // At another position, cannot set
+ return null;
+ }
+ }
+
+ E old = super.set(index, element);
+ itemSet.remove(old);
+ itemSet.add(element);
+
+ return old;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object clone() {
+ ListSet<E> v = (ListSet<E>) super.clone();
+ v.itemSet = new HashSet<E>(itemSet);
+ return v;
+ }
+
+}