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.7KB

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