You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

HierarchicalContainer.java 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.data.util;
  5. import java.util.Collection;
  6. import java.util.Collections;
  7. import java.util.HashSet;
  8. import java.util.Hashtable;
  9. import java.util.LinkedList;
  10. import com.itmill.toolkit.data.Container;
  11. import com.itmill.toolkit.data.Item;
  12. /**
  13. * A specialized Container whose contents can be accessed like it was a
  14. * tree-like structure.
  15. *
  16. * @author IT Mill Ltd.
  17. * @version
  18. * @VERSION@
  19. * @since 3.0
  20. */
  21. public class HierarchicalContainer extends IndexedContainer implements
  22. Container.Hierarchical {
  23. /**
  24. * Set of IDs of those contained Items that can't have children.
  25. */
  26. private final HashSet noChildrenAllowed = new HashSet();
  27. /**
  28. * Mapping from Item ID to parent Item.
  29. */
  30. private final Hashtable parent = new Hashtable();
  31. /**
  32. * Mapping from Item ID to a list of child IDs.
  33. */
  34. private final Hashtable children = new Hashtable();
  35. /**
  36. * List that contains all root elements of the container.
  37. */
  38. private final LinkedList roots = new LinkedList();
  39. /*
  40. * Can the specified Item have any children? Don't add a JavaDoc comment
  41. * here, we use the default documentation from implemented interface.
  42. */
  43. public boolean areChildrenAllowed(Object itemId) {
  44. return !noChildrenAllowed.contains(itemId);
  45. }
  46. /*
  47. * Gets the IDs of the children of the specified Item. Don't add a JavaDoc
  48. * comment here, we use the default documentation from implemented
  49. * interface.
  50. */
  51. public Collection getChildren(Object itemId) {
  52. final Collection c = (Collection) children.get(itemId);
  53. if (c == null) {
  54. return null;
  55. }
  56. return Collections.unmodifiableCollection(c);
  57. }
  58. /*
  59. * Gets the ID of the parent of the specified Item. Don't add a JavaDoc
  60. * comment here, we use the default documentation from implemented
  61. * interface.
  62. */
  63. public Object getParent(Object itemId) {
  64. return parent.get(itemId);
  65. }
  66. /*
  67. * Is the Item corresponding to the given ID a leaf node? Don't add a
  68. * JavaDoc comment here, we use the default documentation from implemented
  69. * interface.
  70. */
  71. public boolean hasChildren(Object itemId) {
  72. return children.get(itemId) != null;
  73. }
  74. /*
  75. * Is the Item corresponding to the given ID a root node? Don't add a
  76. * JavaDoc comment here, we use the default documentation from implemented
  77. * interface.
  78. */
  79. public boolean isRoot(Object itemId) {
  80. return parent.get(itemId) == null;
  81. }
  82. /*
  83. * Gets the IDs of the root elements in the container. Don't add a JavaDoc
  84. * comment here, we use the default documentation from implemented
  85. * interface.
  86. */
  87. public Collection rootItemIds() {
  88. return Collections.unmodifiableCollection(roots);
  89. }
  90. /**
  91. * <p>
  92. * Sets the given Item's capability to have children. If the Item identified
  93. * with the itemId already has children and the areChildrenAllowed is false
  94. * this method fails and <code>false</code> is returned; the children must
  95. * be first explicitly removed with
  96. * {@link #setParent(Object itemId, Object newParentId)} or
  97. * {@link com.itmill.toolkit.data.Container#removeItem(Object itemId)}.
  98. * </p>
  99. *
  100. * @param itemId
  101. * the ID of the Item in the container whose child capability
  102. * is to be set.
  103. * @param childrenAllowed
  104. * the boolean value specifying if the Item can have children
  105. * or not.
  106. * @return <code>true</code> if the operation succeeded,
  107. * <code>false</code> if not
  108. */
  109. public boolean setChildrenAllowed(Object itemId, boolean childrenAllowed) {
  110. // Checks that the item is in the container
  111. if (!containsId(itemId)) {
  112. return false;
  113. }
  114. // Updates status
  115. if (childrenAllowed) {
  116. noChildrenAllowed.remove(itemId);
  117. } else {
  118. noChildrenAllowed.add(itemId);
  119. }
  120. return true;
  121. }
  122. /**
  123. * <p>
  124. * Sets the parent of an Item. The new parent item must exist and be able to
  125. * have children. (<code>canHaveChildren(newParentId) == true</code>).
  126. * It is also possible to detach a node from the hierarchy (and thus make it
  127. * root) by setting the parent <code>null</code>.
  128. * </p>
  129. *
  130. * @param itemId
  131. * the ID of the item to be set as the child of the Item
  132. * identified with newParentId.
  133. * @param newParentId
  134. * the ID of the Item that's to be the new parent of the Item
  135. * identified with itemId.
  136. * @return <code>true</code> if the operation succeeded,
  137. * <code>false</code> if not
  138. */
  139. public boolean setParent(Object itemId, Object newParentId) {
  140. // Checks that the item is in the container
  141. if (!containsId(itemId)) {
  142. return false;
  143. }
  144. // Gets the old parent
  145. final Object oldParentId = parent.get(itemId);
  146. // Checks if no change is necessary
  147. if ((newParentId == null && oldParentId == null)
  148. || newParentId.equals(oldParentId)) {
  149. return true;
  150. }
  151. // Making root
  152. if (newParentId == null) {
  153. // Removes from old parents children list
  154. final LinkedList l = (LinkedList) children.get(itemId);
  155. if (l != null) {
  156. l.remove(itemId);
  157. if (l.isEmpty()) {
  158. children.remove(itemId);
  159. }
  160. }
  161. // Add to be a root
  162. roots.add(itemId);
  163. // Updates parent
  164. parent.remove(itemId);
  165. return true;
  166. }
  167. // Checks that the new parent exists in container and can have
  168. // children
  169. if (!containsId(newParentId) || noChildrenAllowed.contains(newParentId)) {
  170. return false;
  171. }
  172. // Checks that setting parent doesn't result to a loop
  173. Object o = newParentId;
  174. while (o != null && !o.equals(itemId)) {
  175. o = parent.get(o);
  176. }
  177. if (o != null) {
  178. return false;
  179. }
  180. // Updates parent
  181. parent.put(itemId, newParentId);
  182. LinkedList pcl = (LinkedList) children.get(newParentId);
  183. if (pcl == null) {
  184. pcl = new LinkedList();
  185. children.put(newParentId, pcl);
  186. }
  187. pcl.add(itemId);
  188. // Removes from old parent or root
  189. if (oldParentId == null) {
  190. roots.remove(itemId);
  191. } else {
  192. final LinkedList l = (LinkedList) children.get(oldParentId);
  193. if (l != null) {
  194. l.remove(itemId);
  195. if (l.isEmpty()) {
  196. children.remove(oldParentId);
  197. }
  198. }
  199. }
  200. return true;
  201. }
  202. /**
  203. * @see com.itmill.toolkit.data.Container#addItem()
  204. */
  205. public Object addItem() {
  206. final Object id = super.addItem();
  207. if (id != null && !roots.contains(id)) {
  208. roots.add(id);
  209. }
  210. return id;
  211. }
  212. /**
  213. * @see com.itmill.toolkit.data.Container#addItem(Object)
  214. */
  215. public Item addItem(Object itemId) {
  216. final Item item = super.addItem(itemId);
  217. if (item != null) {
  218. roots.add(itemId);
  219. }
  220. return item;
  221. }
  222. /**
  223. * @see com.itmill.toolkit.data.Container#removeAllItems()
  224. */
  225. public boolean removeAllItems() {
  226. final boolean success = super.removeAllItems();
  227. if (success) {
  228. roots.clear();
  229. parent.clear();
  230. children.clear();
  231. noChildrenAllowed.clear();
  232. }
  233. return success;
  234. }
  235. /**
  236. * @see com.itmill.toolkit.data.Container#removeItem(Object)
  237. */
  238. public boolean removeItem(Object itemId) {
  239. final boolean success = super.removeItem(itemId);
  240. if (success) {
  241. if (isRoot(itemId)) {
  242. roots.remove(itemId);
  243. }
  244. children.remove(itemId);
  245. final Object p = parent.get(itemId);
  246. if (p != null) {
  247. final LinkedList c = (LinkedList) children.get(p);
  248. if (c != null) {
  249. c.remove(itemId);
  250. }
  251. }
  252. parent.remove(itemId);
  253. noChildrenAllowed.remove(itemId);
  254. }
  255. return success;
  256. }
  257. }