/* ************************************************************************* IT Mill Toolkit Development of Browser User Interfaces Made Easy Copyright (C) 2000-2006 IT Mill Ltd ************************************************************************* This product is distributed under commercial license that can be found from the product package on license.pdf. Use of this product might require purchasing a commercial license from IT Mill Ltd. For guidelines on usage, see licensing-guidelines.html ************************************************************************* For more information, contact: IT Mill Ltd phone: +358 2 4802 7180 Ruukinkatu 2-4 fax: +358 2 4802 7181 20540, Turku email: info@itmill.com Finland company www: www.itmill.com Primary source for information and releases: www.itmill.com ********************************************************************** */ package com.itmill.toolkit.data.util; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Hashtable; import java.util.LinkedList; import com.itmill.toolkit.data.Container; import com.itmill.toolkit.data.Item; /** * A specialized Container whose contents can be accessed like it was a * tree-like structure. * * @author IT Mill Ltd. * @version * @VERSION@ * @since 3.0 */ public class HierarchicalContainer extends IndexedContainer implements Container.Hierarchical { /** * Set of IDs of those contained Items that can't have children. */ private HashSet noChildrenAllowed = new HashSet(); /** * Mapping from Item ID to parent Item. */ private Hashtable parent = new Hashtable(); /** * Mapping from Item ID to a list of child IDs. */ private Hashtable children = new Hashtable(); /** * List that contains all root elements of the container. */ private LinkedList roots = new LinkedList(); /* * 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); } /* * Gets the IDs of the children of the specified Item. Don't add a JavaDoc * comment here, we use the default documentation from implemented * interface. */ public Collection getChildren(Object itemId) { Collection c = (Collection) children.get(itemId); if (c == null) { return null; } return Collections.unmodifiableCollection(c); } /* * Gets the ID of the parent of the specified Item. Don't add a JavaDoc * comment here, we use the default documentation from implemented * interface. */ public Object getParent(Object itemId) { return parent.get(itemId); } /* * Is the Item corresponding to the given ID a leaf node? Don't add a * JavaDoc comment here, we use the default documentation from implemented * interface. */ public boolean hasChildren(Object itemId) { return children.get(itemId) != null; } /* * Is the Item corresponding to the given ID a root node? Don't add a * JavaDoc comment here, we use the default documentation from implemented * interface. */ public boolean isRoot(Object itemId) { return parent.get(itemId) == null; } /* * Gets the IDs of the root elements in the container. Don't add a JavaDoc * comment here, we use the default documentation from implemented * interface. */ public Collection rootItemIds() { return Collections.unmodifiableCollection(roots); } /** *
* Sets the given Item's capability to have children. If the Item identified
* with the itemId already has children and the areChildrenAllowed is false
* this method fails and false
is returned; the children must
* be first explicitly removed with
* {@link #setParent(Object itemId, Object newParentId)} or
* {@link com.itmill.toolkit.data.Container#removeItem(Object itemId)}.
*
true
if the operation succeeded,
* false
if not
*/
public boolean setChildrenAllowed(Object itemId, boolean childrenAllowed) {
// Checks that the item is in the container
if (!containsId(itemId)) {
return false;
}
// Updates status
if (childrenAllowed) {
noChildrenAllowed.remove(itemId);
} else {
noChildrenAllowed.add(itemId);
}
return true;
}
/**
*
* Sets the parent of an Item. The new parent item must exist and be able to
* have children. (canHaveChildren(newParentId) == true
).
* It is also possible to detach a node from the hierarchy (and thus make it
* root) by setting the parent null
.
*
true
if the operation succeeded,
* false
if not
*/
public boolean setParent(Object itemId, Object newParentId) {
// Checks that the item is in the container
if (!containsId(itemId)) {
return false;
}
// Gets the old parent
Object oldParentId = parent.get(itemId);
// Checks if no change is necessary
if ((newParentId == null && oldParentId == null)
|| newParentId.equals(oldParentId)) {
return true;
}
// Making root
if (newParentId == null) {
// Removes from old parents children list
LinkedList l = (LinkedList) children.get(itemId);
if (l != null) {
l.remove(itemId);
if (l.isEmpty()) {
children.remove(itemId);
}
}
// Add to be a root
roots.add(itemId);
// Updates parent
parent.remove(itemId);
return true;
}
// Checks that the new parent exists in container and can have
// children
if (!containsId(newParentId) || noChildrenAllowed.contains(newParentId)) {
return false;
}
// Checks that setting parent doesn't result to a loop
Object o = newParentId;
while (o != null && !o.equals(itemId)) {
o = parent.get(o);
}
if (o != null) {
return false;
}
// Updates parent
parent.put(itemId, newParentId);
LinkedList pcl = (LinkedList) children.get(newParentId);
if (pcl == null) {
pcl = new LinkedList();
children.put(newParentId, pcl);
}
pcl.add(itemId);
// Removes from old parent or root
if (oldParentId == null) {
roots.remove(itemId);
} else {
LinkedList l = (LinkedList) children.get(oldParentId);
if (l != null) {
l.remove(itemId);
if (l.isEmpty()) {
children.remove(oldParentId);
}
}
}
return true;
}
/**
* @see com.itmill.toolkit.data.Container#addItem()
*/
public Object addItem() {
Object id = super.addItem();
if (id != null && !roots.contains(id)) {
roots.add(id);
}
return id;
}
/**
* @see com.itmill.toolkit.data.Container#addItem(Object)
*/
public Item addItem(Object itemId) {
Item item = super.addItem(itemId);
if (item != null) {
roots.add(itemId);
}
return item;
}
/**
* @see com.itmill.toolkit.data.Container#removeAllItems()
*/
public boolean removeAllItems() {
boolean success = super.removeAllItems();
if (success) {
roots.clear();
parent.clear();
children.clear();
noChildrenAllowed.clear();
}
return success;
}
/**
* @see com.itmill.toolkit.data.Container#removeItem(Object)
*/
public boolean removeItem(Object itemId) {
boolean success = super.removeItem(itemId);
if (success) {
if (isRoot(itemId)) {
roots.remove(itemId);
}
children.remove(itemId);
Object p = parent.get(itemId);
if (p != null) {
LinkedList c = (LinkedList) children.get(p);
if (c != null) {
c.remove(itemId);
}
}
parent.remove(itemId);
noChildrenAllowed.remove(itemId);
}
return success;
}
}