/* @ITMillApache2LicenseForJavaFiles@ */ package com.vaadin.data.util; import java.util.Collection; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.Property; /** *
* A wrapper class for adding external ordering to containers not implementing * the {@link com.vaadin.data.Container.Ordered} interface. *
* ** If the wrapped container is changed directly (that is, not through the * wrapper), and does not implement Container.ItemSetChangeNotifier and/or * Container.PropertySetChangeNotifier the hierarchy information must be updated * with the {@link #updateOrderWrapper()} method. *
* * @author IT Mill Ltd. * @version * @VERSION@ * @since 3.0 */ @SuppressWarnings("serial") public class ContainerOrderedWrapper implements Container.Ordered, Container.ItemSetChangeNotifier, Container.PropertySetChangeNotifier { /** * The wrapped container */ private final Container container; /** * Ordering information, ie. the mapping from Item ID to the next item ID */ private Hashtable next; /** * Reverse ordering information for convenience and performance reasons. */ private Hashtable prev; /** * ID of the first Item in the container. */ private Object first; /** * ID of the last Item in the container. */ private Object last; /** * Is the wrapped container ordered by itself, ie. does it implement the * Container.Ordered interface by itself? If it does, this class will use * the methods of the underlying container directly. */ private boolean ordered = false; /** * Constructs a new ordered wrapper for an existing Container. Works even if * the to-be-wrapped container already implements the Container.Ordered * interface. * * @param toBeWrapped * the container whose contents need to be ordered. */ public ContainerOrderedWrapper(Container toBeWrapped) { container = toBeWrapped; ordered = container instanceof Container.Ordered; // Checks arguments if (container == null) { throw new NullPointerException("Null can not be wrapped"); } // Creates initial order if needed updateOrderWrapper(); } /** * Removes the specified Item from the wrapper's internal hierarchy * structure. ** Note : The Item is not removed from the underlying Container. *
* * @param id * the ID of the Item to be removed from the ordering. */ private void removeFromOrderWrapper(Object id) { if (id != null) { final Object pid = prev.get(id); final Object nid = next.get(id); if (first.equals(id)) { first = nid; } if (last.equals(id)) { first = pid; } if (nid != null) { prev.put(nid, pid); } if (pid != null) { next.put(pid, nid); } next.remove(id); prev.remove(id); } } /** * Registers the specified Item to the last position in the wrapper's * internal ordering. The underlying container is not modified. * * @param id * the ID of the Item to be added to the ordering. */ private void addToOrderWrapper(Object id) { // Adds the if to tail if (last != null) { next.put(last, id); prev.put(id, last); last = id; } else { first = last = id; } } /** * Registers the specified Item after the specified itemId in the wrapper's * internal ordering. The underlying container is not modified. Given item * id must be in the container, or must be null. * * @param id * the ID of the Item to be added to the ordering. * @param previousItemId * the Id of the previous item. */ private void addToOrderWrapper(Object id, Object previousItemId) { if (last == previousItemId || last == null) { addToOrderWrapper(id); } else { if (previousItemId == null) { next.put(id, first); prev.put(first, id); first = id; } else { prev.put(id, previousItemId); next.put(id, next.get(previousItemId)); prev.put(next.get(previousItemId), id); next.put(previousItemId, id); } } } /** * Updates the wrapper's internal ordering information to include all Items * in the underlying container. ** Note : If the contents of the wrapped container change without the * wrapper's knowledge, this method needs to be called to update the * ordering information of the Items. *
*/ public void updateOrderWrapper() { if (!ordered) { final Collection ids = container.getItemIds(); // Recreates ordering if some parts of it are missing if (next == null || first == null || last == null || prev != null) { first = null; last = null; next = new Hashtable(); prev = new Hashtable(); } // Filter out all the missing items final LinkedList l = new LinkedList(next.keySet()); for (final Iterator i = l.iterator(); i.hasNext();) { final Object id = i.next(); if (!container.containsId(id)) { removeFromOrderWrapper(id); } } // Adds missing items for (final Iterator i = ids.iterator(); i.hasNext();) { final Object id = i.next(); if (!next.containsKey(id)) { addToOrderWrapper(id); } } } } /* * Gets the first item stored in the ordered container Don't add a JavaDoc * comment here, we use the default documentation from implemented * interface. */ public Object firstItemId() { if (ordered) { return ((Container.Ordered) container).firstItemId(); } return first; } /* * Tests if the given item is the first item in the container Don't add a * JavaDoc comment here, we use the default documentation from implemented * interface. */ public boolean isFirstId(Object itemId) { if (ordered) { return ((Container.Ordered) container).isFirstId(itemId); } return first != null && first.equals(itemId); } /* * Tests if the given item is the last item in the container Don't add a * JavaDoc comment here, we use the default documentation from implemented * interface. */ public boolean isLastId(Object itemId) { if (ordered) { return ((Container.Ordered) container).isLastId(itemId); } return last != null && last.equals(itemId); } /* * Gets the last item stored in the ordered container Don't add a JavaDoc * comment here, we use the default documentation from implemented * interface. */ public Object lastItemId() { if (ordered) { return ((Container.Ordered) container).lastItemId(); } return last; } /* * Gets the item that is next from the specified item. Don't add a JavaDoc * comment here, we use the default documentation from implemented * interface. */ public Object nextItemId(Object itemId) { if (ordered) { return ((Container.Ordered) container).nextItemId(itemId); } if (itemId == null) { return null; } return next.get(itemId); } /* * Gets the item that is previous from the specified item. Don't add a * JavaDoc comment here, we use the default documentation from implemented * interface. */ public Object prevItemId(Object itemId) { if (ordered) { return ((Container.Ordered) container).prevItemId(itemId); } if (itemId == null) { return null; } return prev.get(itemId); } /** * Registers a new Property to all Items in the Container. * * @param propertyId * the ID of the new Property. * @param type * the Data type of the new Property. * @param defaultValue * the value all created Properties are initialized to. * @returntrue
if the operation succeeded, false
* if not
*/
public boolean addContainerProperty(Object propertyId, Class type,
Object defaultValue) throws UnsupportedOperationException {
return container.addContainerProperty(propertyId, type, defaultValue);
}
/**
* Creates a new Item into the Container, assigns it an automatic ID, and
* adds it to the ordering.
*
* @return the autogenerated ID of the new Item or null
if the
* operation failed
* @throws UnsupportedOperationException
* if the addItem is not supported.
*/
public Object addItem() throws UnsupportedOperationException {
final Object id = container.addItem();
if (!ordered && id != null) {
addToOrderWrapper(id);
}
return id;
}
/**
* Registers a new Item by its ID to the underlying container and to the
* ordering.
*
* @param itemId
* the ID of the Item to be created.
* @return the added Item or null
if the operation failed
* @throws UnsupportedOperationException
* if the addItem is not supported.
*/
public Item addItem(Object itemId) throws UnsupportedOperationException {
final Item item = container.addItem(itemId);
if (!ordered && item != null) {
addToOrderWrapper(itemId);
}
return item;
}
/**
* Removes all items from the underlying container and from the ordering.
*
* @return true
if the operation succeeded, otherwise
* false
* @throws UnsupportedOperationException
* if the removeAllItems is not supported.
*/
public boolean removeAllItems() throws UnsupportedOperationException {
final boolean success = container.removeAllItems();
if (!ordered && success) {
first = last = null;
next.clear();
prev.clear();
}
return success;
}
/**
* Removes an Item specified by the itemId from the underlying container and
* from the ordering.
*
* @param itemId
* the ID of the Item to be removed.
* @return true
if the operation succeeded, false
* if not
* @throws UnsupportedOperationException
* if the removeItem is not supported.
*/
public boolean removeItem(Object itemId)
throws UnsupportedOperationException {
final boolean success = container.removeItem(itemId);
if (!ordered && success) {
removeFromOrderWrapper(itemId);
}
return success;
}
/**
* Removes the specified Property from the underlying container and from the
* ordering.
* * Note : The Property will be removed from all the Items in the Container. *
* * @param propertyId * the ID of the Property to remove. * @returntrue
if the operation succeeded, false
* if not
* @throws UnsupportedOperationException
* if the removeContainerProperty is not supported.
*/
public boolean removeContainerProperty(Object propertyId)
throws UnsupportedOperationException {
return container.removeContainerProperty(propertyId);
}
/*
* Does the container contain the specified Item? Don't add a JavaDoc
* comment here, we use the default documentation from implemented
* interface.
*/
public boolean containsId(Object itemId) {
return container.containsId(itemId);
}
/*
* Gets the specified Item from the container. Don't add a JavaDoc comment
* here, we use the default documentation from implemented interface.
*/
public Item getItem(Object itemId) {
return container.getItem(itemId);
}
/*
* Gets the ID's of all Items stored in the Container Don't add a JavaDoc
* comment here, we use the default documentation from implemented
* interface.
*/
public Collection getItemIds() {
return container.getItemIds();
}
/*
* Gets the Property identified by the given itemId and propertyId from the
* Container Don't add a JavaDoc comment here, we use the default
* documentation from implemented interface.
*/
public Property getContainerProperty(Object itemId, Object propertyId) {
return container.getContainerProperty(itemId, propertyId);
}
/*
* Gets the ID's of all Properties stored in the Container Don't add a
* JavaDoc comment here, we use the default documentation from implemented
* interface.
*/
public Collection getContainerPropertyIds() {
return container.getContainerPropertyIds();
}
/*
* Gets the data type of all Properties identified by the given Property ID.
* Don't add a JavaDoc comment here, we use the default documentation from
* implemented interface.
*/
public Class getType(Object propertyId) {
return container.getType(propertyId);
}
/*
* Gets the number of Items in the Container. Don't add a JavaDoc comment
* here, we use the default documentation from implemented interface.
*/
public int size() {
return container.size();
}
/*
* Registers a new Item set change listener for this Container. Don't add a
* JavaDoc comment here, we use the default documentation from implemented
* interface.
*/
public void addListener(Container.ItemSetChangeListener listener) {
if (container instanceof Container.ItemSetChangeNotifier) {
((Container.ItemSetChangeNotifier) container)
.addListener(new PiggybackListener(listener));
}
}
/*
* Removes a Item set change listener from the object. Don't add a JavaDoc
* comment here, we use the default documentation from implemented
* interface.
*/
public void removeListener(Container.ItemSetChangeListener listener) {
if (container instanceof Container.ItemSetChangeNotifier) {
((Container.ItemSetChangeNotifier) container)
.removeListener(new PiggybackListener(listener));
}
}
/*
* Registers a new Property set change listener for this Container. Don't
* add a JavaDoc comment here, we use the default documentation from
* implemented interface.
*/
public void addListener(Container.PropertySetChangeListener listener) {
if (container instanceof Container.PropertySetChangeNotifier) {
((Container.PropertySetChangeNotifier) container)
.addListener(new PiggybackListener(listener));
}
}
/*
* Removes a Property set change listener from the object. Don't add a
* JavaDoc comment here, we use the default documentation from implemented
* interface.
*/
public void removeListener(Container.PropertySetChangeListener listener) {
if (container instanceof Container.PropertySetChangeNotifier) {
((Container.PropertySetChangeNotifier) container)
.removeListener(new PiggybackListener(listener));
}
}
/*
* (non-Javadoc)
*
* @see
* com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
* java.lang.Object)
*/
public Item addItemAfter(Object previousItemId, Object newItemId)
throws UnsupportedOperationException {
// If the previous item is not in the container, fail
if (previousItemId != null && !containsId(previousItemId)) {
return null;
}
// Adds the item to container
final Item item = container.addItem(newItemId);
// Puts the new item to its correct place
if (!ordered && item != null) {
addToOrderWrapper(newItemId, previousItemId);
}
return item;
}
/*
* (non-Javadoc)
*
* @see
* com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
*/
public Object addItemAfter(Object previousItemId)
throws UnsupportedOperationException {
// If the previous item is not in the container, fail
if (previousItemId != null && !containsId(previousItemId)) {
return null;
}
// Adds the item to container
final Object id = container.addItem();
// Puts the new item to its correct place
if (!ordered && id != null) {
addToOrderWrapper(id, previousItemId);
}
return id;
}
/**
* This listener 'piggybacks' on the real listener in order to update the
* wrapper when needed. It proxies equals() and hashCode() to the real
* listener so that the correct listener gets removed.
*
*/
private class PiggybackListener implements
Container.PropertySetChangeListener,
Container.ItemSetChangeListener {
Object listener;
public PiggybackListener(Object realListener) {
listener = realListener;
}
public void containerItemSetChange(ItemSetChangeEvent event) {
updateOrderWrapper();
((Container.ItemSetChangeListener) listener)
.containerItemSetChange(event);
}
public void containerPropertySetChange(PropertySetChangeEvent event) {
updateOrderWrapper();
((Container.PropertySetChangeListener) listener)
.containerPropertySetChange(event);
}
@Override
public boolean equals(Object obj) {
return obj == listener || (obj != null && obj.equals(listener));
}
@Override
public int hashCode() {
return listener.hashCode();
}
}
}