123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- /*
- @ITMillApache2LicenseForJavaFiles@
- */
-
- 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 final HashSet noChildrenAllowed = new HashSet();
-
- /**
- * Mapping from Item ID to parent Item.
- */
- private final Hashtable parent = new Hashtable();
-
- /**
- * Mapping from Item ID to a list of child IDs.
- */
- private final Hashtable children = new Hashtable();
-
- /**
- * List that contains all root elements of the container.
- */
- private final 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) {
- final 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);
- }
-
- /**
- * <p>
- * 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 <code>false</code> 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)}.
- * </p>
- *
- * @param itemId
- * the ID of the Item in the container whose child capability
- * is to be set.
- * @param childrenAllowed
- * the boolean value specifying if the Item can have children
- * or not.
- * @return <code>true</code> if the operation succeeded,
- * <code>false</code> 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;
- }
-
- /**
- * <p>
- * Sets the parent of an Item. The new parent item must exist and be able to
- * have children. (<code>canHaveChildren(newParentId) == true</code>).
- * It is also possible to detach a node from the hierarchy (and thus make it
- * root) by setting the parent <code>null</code>.
- * </p>
- *
- * @param itemId
- * the ID of the item to be set as the child of the Item
- * identified with newParentId.
- * @param newParentId
- * the ID of the Item that's to be the new parent of the Item
- * identified with itemId.
- * @return <code>true</code> if the operation succeeded,
- * <code>false</code> 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
- final 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
- final 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 {
- final 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() {
- final 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) {
- final Item item = super.addItem(itemId);
- if (item != null) {
- roots.add(itemId);
- }
- return item;
- }
-
- /**
- * @see com.itmill.toolkit.data.Container#removeAllItems()
- */
- public boolean removeAllItems() {
- final 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) {
- final boolean success = super.removeItem(itemId);
-
- if (success) {
- if (isRoot(itemId)) {
- roots.remove(itemId);
- }
- children.remove(itemId);
- final Object p = parent.get(itemId);
- if (p != null) {
- final LinkedList c = (LinkedList) children.get(p);
- if (c != null) {
- c.remove(itemId);
- }
- }
- parent.remove(itemId);
- noChildrenAllowed.remove(itemId);
- }
-
- return success;
- }
-
- }
|