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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.data.util;
  5. import java.util.Collection;
  6. import java.util.Collections;
  7. import java.util.HashMap;
  8. import java.util.HashSet;
  9. import java.util.LinkedHashSet;
  10. import java.util.LinkedList;
  11. import java.util.Set;
  12. import com.vaadin.data.Container;
  13. import com.vaadin.data.Item;
  14. /**
  15. * A specialized Container whose contents can be accessed like it was a
  16. * tree-like structure.
  17. *
  18. * @author Vaadin Ltd.
  19. * @version
  20. * @VERSION@
  21. * @since 3.0
  22. */
  23. @SuppressWarnings("serial")
  24. public class HierarchicalContainer extends IndexedContainer implements
  25. Container.Hierarchical {
  26. /**
  27. * Set of IDs of those contained Items that can't have children.
  28. */
  29. private final HashSet<Object> noChildrenAllowed = new HashSet<Object>();
  30. /**
  31. * Mapping from Item ID to parent Item ID.
  32. */
  33. private final HashMap<Object, Object> parent = new HashMap<Object, Object>();
  34. /**
  35. * Mapping from Item ID to parent Item ID for items included in the filtered
  36. * container.
  37. */
  38. private HashMap<Object, Object> filteredParent = null;
  39. /**
  40. * Mapping from Item ID to a list of child IDs.
  41. */
  42. private final HashMap<Object, LinkedList<Object>> children = new HashMap<Object, LinkedList<Object>>();
  43. /**
  44. * Mapping from Item ID to a list of child IDs when filtered
  45. */
  46. private HashMap<Object, LinkedList<Object>> filteredChildren = null;
  47. /**
  48. * List that contains all root elements of the container.
  49. */
  50. private final LinkedList<Object> roots = new LinkedList<Object>();
  51. /**
  52. * List that contains all filtered root elements of the container.
  53. */
  54. private LinkedList<Object> filteredRoots = null;
  55. /**
  56. * Determines how filtering of the container is done.
  57. */
  58. private boolean includeParentsWhenFiltering = true;
  59. private boolean contentChangedEventsDisabled = false;
  60. private boolean contentsChangedEventPending;
  61. /*
  62. * Can the specified Item have any children? Don't add a JavaDoc comment
  63. * here, we use the default documentation from implemented interface.
  64. */
  65. @Override
  66. public boolean areChildrenAllowed(Object itemId) {
  67. if (noChildrenAllowed.contains(itemId)) {
  68. return false;
  69. }
  70. return containsId(itemId);
  71. }
  72. /*
  73. * Gets the IDs of the children of the specified Item. Don't add a JavaDoc
  74. * comment here, we use the default documentation from implemented
  75. * interface.
  76. */
  77. @Override
  78. public Collection<?> getChildren(Object itemId) {
  79. LinkedList<Object> c;
  80. if (filteredChildren != null) {
  81. c = filteredChildren.get(itemId);
  82. } else {
  83. c = children.get(itemId);
  84. }
  85. if (c == null) {
  86. return null;
  87. }
  88. return Collections.unmodifiableCollection(c);
  89. }
  90. /*
  91. * Gets the ID of the parent of the specified Item. Don't add a JavaDoc
  92. * comment here, we use the default documentation from implemented
  93. * interface.
  94. */
  95. @Override
  96. public Object getParent(Object itemId) {
  97. if (filteredParent != null) {
  98. return filteredParent.get(itemId);
  99. }
  100. return parent.get(itemId);
  101. }
  102. /*
  103. * Is the Item corresponding to the given ID a leaf node? Don't add a
  104. * JavaDoc comment here, we use the default documentation from implemented
  105. * interface.
  106. */
  107. @Override
  108. public boolean hasChildren(Object itemId) {
  109. if (filteredChildren != null) {
  110. return filteredChildren.containsKey(itemId);
  111. } else {
  112. return children.containsKey(itemId);
  113. }
  114. }
  115. /*
  116. * Is the Item corresponding to the given ID a root node? Don't add a
  117. * JavaDoc comment here, we use the default documentation from implemented
  118. * interface.
  119. */
  120. @Override
  121. public boolean isRoot(Object itemId) {
  122. // If the container is filtered the itemId must be among filteredRoots
  123. // to be a root.
  124. if (filteredRoots != null) {
  125. if (!filteredRoots.contains(itemId)) {
  126. return false;
  127. }
  128. } else {
  129. // Container is not filtered
  130. if (parent.containsKey(itemId)) {
  131. return false;
  132. }
  133. }
  134. return containsId(itemId);
  135. }
  136. /*
  137. * Gets the IDs of the root elements in the container. Don't add a JavaDoc
  138. * comment here, we use the default documentation from implemented
  139. * interface.
  140. */
  141. @Override
  142. public Collection<?> rootItemIds() {
  143. if (filteredRoots != null) {
  144. return Collections.unmodifiableCollection(filteredRoots);
  145. } else {
  146. return Collections.unmodifiableCollection(roots);
  147. }
  148. }
  149. /**
  150. * <p>
  151. * Sets the given Item's capability to have children. If the Item identified
  152. * with the itemId already has children and the areChildrenAllowed is false
  153. * this method fails and <code>false</code> is returned; the children must
  154. * be first explicitly removed with
  155. * {@link #setParent(Object itemId, Object newParentId)} or
  156. * {@link com.vaadin.data.Container#removeItem(Object itemId)}.
  157. * </p>
  158. *
  159. * @param itemId
  160. * the ID of the Item in the container whose child capability is
  161. * to be set.
  162. * @param childrenAllowed
  163. * the boolean value specifying if the Item can have children or
  164. * not.
  165. * @return <code>true</code> if the operation succeeded, <code>false</code>
  166. * if not
  167. */
  168. @Override
  169. public boolean setChildrenAllowed(Object itemId, boolean childrenAllowed) {
  170. // Checks that the item is in the container
  171. if (!containsId(itemId)) {
  172. return false;
  173. }
  174. // Updates status
  175. if (childrenAllowed) {
  176. noChildrenAllowed.remove(itemId);
  177. } else {
  178. noChildrenAllowed.add(itemId);
  179. }
  180. return true;
  181. }
  182. /**
  183. * <p>
  184. * Sets the parent of an Item. The new parent item must exist and be able to
  185. * have children. (<code>canHaveChildren(newParentId) == true</code>). It is
  186. * also possible to detach a node from the hierarchy (and thus make it root)
  187. * by setting the parent <code>null</code>.
  188. * </p>
  189. *
  190. * @param itemId
  191. * the ID of the item to be set as the child of the Item
  192. * identified with newParentId.
  193. * @param newParentId
  194. * the ID of the Item that's to be the new parent of the Item
  195. * identified with itemId.
  196. * @return <code>true</code> if the operation succeeded, <code>false</code>
  197. * if not
  198. */
  199. @Override
  200. public boolean setParent(Object itemId, Object newParentId) {
  201. // Checks that the item is in the container
  202. if (!containsId(itemId)) {
  203. return false;
  204. }
  205. // Gets the old parent
  206. final Object oldParentId = parent.get(itemId);
  207. // Checks if no change is necessary
  208. if ((newParentId == null && oldParentId == null)
  209. || ((newParentId != null) && newParentId.equals(oldParentId))) {
  210. return true;
  211. }
  212. // Making root?
  213. if (newParentId == null) {
  214. // The itemId should become a root so we need to
  215. // - Remove it from the old parent's children list
  216. // - Add it as a root
  217. // - Remove it from the item -> parent list (parent is null for
  218. // roots)
  219. // Removes from old parents children list
  220. final LinkedList<Object> l = children.get(oldParentId);
  221. if (l != null) {
  222. l.remove(itemId);
  223. if (l.isEmpty()) {
  224. children.remove(oldParentId);
  225. }
  226. }
  227. // Add to be a root
  228. roots.add(itemId);
  229. // Updates parent
  230. parent.remove(itemId);
  231. if (hasFilters()) {
  232. // Refilter the container if setParent is called when filters
  233. // are applied. Changing parent can change what is included in
  234. // the filtered version (if includeParentsWhenFiltering==true).
  235. doFilterContainer(hasFilters());
  236. }
  237. fireItemSetChange();
  238. return true;
  239. }
  240. // We get here when the item should not become a root and we need to
  241. // - Verify the new parent exists and can have children
  242. // - Check that the new parent is not a child of the selected itemId
  243. // - Updated the item -> parent mapping to point to the new parent
  244. // - Remove the item from the roots list if it was a root
  245. // - Remove the item from the old parent's children list if it was not a
  246. // root
  247. // Checks that the new parent exists in container and can have
  248. // children
  249. if (!containsId(newParentId) || noChildrenAllowed.contains(newParentId)) {
  250. return false;
  251. }
  252. // Checks that setting parent doesn't result to a loop
  253. Object o = newParentId;
  254. while (o != null && !o.equals(itemId)) {
  255. o = parent.get(o);
  256. }
  257. if (o != null) {
  258. return false;
  259. }
  260. // Updates parent
  261. parent.put(itemId, newParentId);
  262. LinkedList<Object> pcl = children.get(newParentId);
  263. if (pcl == null) {
  264. // Create an empty list for holding children if one were not
  265. // previously created
  266. pcl = new LinkedList<Object>();
  267. children.put(newParentId, pcl);
  268. }
  269. pcl.add(itemId);
  270. // Removes from old parent or root
  271. if (oldParentId == null) {
  272. roots.remove(itemId);
  273. } else {
  274. final LinkedList<Object> l = children.get(oldParentId);
  275. if (l != null) {
  276. l.remove(itemId);
  277. if (l.isEmpty()) {
  278. children.remove(oldParentId);
  279. }
  280. }
  281. }
  282. if (hasFilters()) {
  283. // Refilter the container if setParent is called when filters
  284. // are applied. Changing parent can change what is included in
  285. // the filtered version (if includeParentsWhenFiltering==true).
  286. doFilterContainer(hasFilters());
  287. }
  288. fireItemSetChange();
  289. return true;
  290. }
  291. private boolean hasFilters() {
  292. return (filteredRoots != null);
  293. }
  294. /**
  295. * Moves a node (an Item) in the container immediately after a sibling node.
  296. * The two nodes must have the same parent in the container.
  297. *
  298. * @param itemId
  299. * the identifier of the moved node (Item)
  300. * @param siblingId
  301. * the identifier of the reference node (Item), after which the
  302. * other node will be located
  303. */
  304. public void moveAfterSibling(Object itemId, Object siblingId) {
  305. Object parent2 = getParent(itemId);
  306. LinkedList<Object> childrenList;
  307. if (parent2 == null) {
  308. childrenList = roots;
  309. } else {
  310. childrenList = children.get(parent2);
  311. }
  312. if (siblingId == null) {
  313. childrenList.remove(itemId);
  314. childrenList.addFirst(itemId);
  315. } else {
  316. int oldIndex = childrenList.indexOf(itemId);
  317. int indexOfSibling = childrenList.indexOf(siblingId);
  318. if (indexOfSibling != -1 && oldIndex != -1) {
  319. int newIndex;
  320. if (oldIndex > indexOfSibling) {
  321. newIndex = indexOfSibling + 1;
  322. } else {
  323. newIndex = indexOfSibling;
  324. }
  325. childrenList.remove(oldIndex);
  326. childrenList.add(newIndex, itemId);
  327. } else {
  328. throw new IllegalArgumentException(
  329. "Given identifiers no not have the same parent.");
  330. }
  331. }
  332. fireItemSetChange();
  333. }
  334. /*
  335. * (non-Javadoc)
  336. *
  337. * @see com.vaadin.data.util.IndexedContainer#addItem()
  338. */
  339. @Override
  340. public Object addItem() {
  341. disableContentsChangeEvents();
  342. final Object itemId = super.addItem();
  343. if (itemId == null) {
  344. return null;
  345. }
  346. if (!roots.contains(itemId)) {
  347. roots.add(itemId);
  348. if (filteredRoots != null) {
  349. if (passesFilters(itemId)) {
  350. filteredRoots.add(itemId);
  351. }
  352. }
  353. }
  354. enableAndFireContentsChangeEvents();
  355. return itemId;
  356. }
  357. @Override
  358. protected void fireItemSetChange(
  359. com.vaadin.data.Container.ItemSetChangeEvent event) {
  360. if (contentsChangeEventsOn()) {
  361. super.fireItemSetChange(event);
  362. } else {
  363. contentsChangedEventPending = true;
  364. }
  365. }
  366. private boolean contentsChangeEventsOn() {
  367. return !contentChangedEventsDisabled;
  368. }
  369. private void disableContentsChangeEvents() {
  370. contentChangedEventsDisabled = true;
  371. }
  372. private void enableAndFireContentsChangeEvents() {
  373. contentChangedEventsDisabled = false;
  374. if (contentsChangedEventPending) {
  375. fireItemSetChange();
  376. }
  377. contentsChangedEventPending = false;
  378. }
  379. /*
  380. * (non-Javadoc)
  381. *
  382. * @see com.vaadin.data.util.IndexedContainer#addItem(java.lang.Object)
  383. */
  384. @Override
  385. public Item addItem(Object itemId) {
  386. disableContentsChangeEvents();
  387. final Item item = super.addItem(itemId);
  388. if (item == null) {
  389. return null;
  390. }
  391. roots.add(itemId);
  392. if (filteredRoots != null) {
  393. if (passesFilters(itemId)) {
  394. filteredRoots.add(itemId);
  395. }
  396. }
  397. enableAndFireContentsChangeEvents();
  398. return item;
  399. }
  400. /*
  401. * (non-Javadoc)
  402. *
  403. * @see com.vaadin.data.util.IndexedContainer#removeAllItems()
  404. */
  405. @Override
  406. public boolean removeAllItems() {
  407. disableContentsChangeEvents();
  408. final boolean success = super.removeAllItems();
  409. if (success) {
  410. roots.clear();
  411. parent.clear();
  412. children.clear();
  413. noChildrenAllowed.clear();
  414. if (filteredRoots != null) {
  415. filteredRoots = null;
  416. }
  417. if (filteredChildren != null) {
  418. filteredChildren = null;
  419. }
  420. if (filteredParent != null) {
  421. filteredParent = null;
  422. }
  423. }
  424. enableAndFireContentsChangeEvents();
  425. return success;
  426. }
  427. /*
  428. * (non-Javadoc)
  429. *
  430. * @see com.vaadin.data.util.IndexedContainer#removeItem(java.lang.Object )
  431. */
  432. @Override
  433. public boolean removeItem(Object itemId) {
  434. disableContentsChangeEvents();
  435. final boolean success = super.removeItem(itemId);
  436. if (success) {
  437. // Remove from roots if this was a root
  438. if (roots.remove(itemId)) {
  439. // If filtering is enabled we might need to remove it from the
  440. // filtered list also
  441. if (filteredRoots != null) {
  442. filteredRoots.remove(itemId);
  443. }
  444. }
  445. // Clear the children list. Old children will now become root nodes
  446. LinkedList<Object> childNodeIds = children.remove(itemId);
  447. if (childNodeIds != null) {
  448. if (filteredChildren != null) {
  449. filteredChildren.remove(itemId);
  450. }
  451. for (Object childId : childNodeIds) {
  452. setParent(childId, null);
  453. }
  454. }
  455. // Parent of the item that we are removing will contain the item id
  456. // in its children list
  457. final Object parentItemId = parent.get(itemId);
  458. if (parentItemId != null) {
  459. final LinkedList<Object> c = children.get(parentItemId);
  460. if (c != null) {
  461. c.remove(itemId);
  462. if (c.isEmpty()) {
  463. children.remove(parentItemId);
  464. }
  465. // Found in the children list so might also be in the
  466. // filteredChildren list
  467. if (filteredChildren != null) {
  468. LinkedList<Object> f = filteredChildren
  469. .get(parentItemId);
  470. if (f != null) {
  471. f.remove(itemId);
  472. if (f.isEmpty()) {
  473. filteredChildren.remove(parentItemId);
  474. }
  475. }
  476. }
  477. }
  478. }
  479. parent.remove(itemId);
  480. if (filteredParent != null) {
  481. // Item id no longer has a parent as the item id is not in the
  482. // container.
  483. filteredParent.remove(itemId);
  484. }
  485. noChildrenAllowed.remove(itemId);
  486. }
  487. enableAndFireContentsChangeEvents();
  488. return success;
  489. }
  490. /**
  491. * Removes the Item identified by given itemId and all its children.
  492. *
  493. * @see #removeItem(Object)
  494. * @param itemId
  495. * the identifier of the Item to be removed
  496. * @return true if the operation succeeded
  497. */
  498. public boolean removeItemRecursively(Object itemId) {
  499. disableContentsChangeEvents();
  500. boolean removeItemRecursively = removeItemRecursively(this, itemId);
  501. enableAndFireContentsChangeEvents();
  502. return removeItemRecursively;
  503. }
  504. /**
  505. * Removes the Item identified by given itemId and all its children from the
  506. * given Container.
  507. *
  508. * @param container
  509. * the container where the item is to be removed
  510. * @param itemId
  511. * the identifier of the Item to be removed
  512. * @return true if the operation succeeded
  513. */
  514. public static boolean removeItemRecursively(
  515. Container.Hierarchical container, Object itemId) {
  516. boolean success = true;
  517. Collection<?> children2 = container.getChildren(itemId);
  518. if (children2 != null) {
  519. Object[] array = children2.toArray();
  520. for (int i = 0; i < array.length; i++) {
  521. boolean removeItemRecursively = removeItemRecursively(
  522. container, array[i]);
  523. if (!removeItemRecursively) {
  524. success = false;
  525. }
  526. }
  527. }
  528. // remove the root of subtree if children where succesfully removed
  529. if (success) {
  530. success = container.removeItem(itemId);
  531. }
  532. return success;
  533. }
  534. /*
  535. * (non-Javadoc)
  536. *
  537. * @see com.vaadin.data.util.IndexedContainer#doSort()
  538. */
  539. @Override
  540. protected void doSort() {
  541. super.doSort();
  542. Collections.sort(roots, getItemSorter());
  543. for (LinkedList<Object> childList : children.values()) {
  544. Collections.sort(childList, getItemSorter());
  545. }
  546. }
  547. /**
  548. * Used to control how filtering works. @see
  549. * {@link #setIncludeParentsWhenFiltering(boolean)} for more information.
  550. *
  551. * @return true if all parents for items that match the filter are included
  552. * when filtering, false if only the matching items are included
  553. */
  554. public boolean isIncludeParentsWhenFiltering() {
  555. return includeParentsWhenFiltering;
  556. }
  557. /**
  558. * Controls how the filtering of the container works. Set this to true to
  559. * make filtering include parents for all matched items in addition to the
  560. * items themselves. Setting this to false causes the filtering to only
  561. * include the matching items and make items with excluded parents into root
  562. * items.
  563. *
  564. * @param includeParentsWhenFiltering
  565. * true to include all parents for items that match the filter,
  566. * false to only include the matching items
  567. */
  568. public void setIncludeParentsWhenFiltering(
  569. boolean includeParentsWhenFiltering) {
  570. this.includeParentsWhenFiltering = includeParentsWhenFiltering;
  571. if (filteredRoots != null) {
  572. // Currently filtered so needs to be re-filtered
  573. doFilterContainer(true);
  574. }
  575. }
  576. /*
  577. * Overridden to provide filtering for root & children items.
  578. *
  579. * (non-Javadoc)
  580. *
  581. * @see com.vaadin.data.util.IndexedContainer#updateContainerFiltering()
  582. */
  583. @Override
  584. protected boolean doFilterContainer(boolean hasFilters) {
  585. if (!hasFilters) {
  586. // All filters removed
  587. filteredRoots = null;
  588. filteredChildren = null;
  589. filteredParent = null;
  590. return super.doFilterContainer(hasFilters);
  591. }
  592. // Reset data structures
  593. filteredRoots = new LinkedList<Object>();
  594. filteredChildren = new HashMap<Object, LinkedList<Object>>();
  595. filteredParent = new HashMap<Object, Object>();
  596. if (includeParentsWhenFiltering) {
  597. // Filter so that parents for items that match the filter are also
  598. // included
  599. HashSet<Object> includedItems = new HashSet<Object>();
  600. for (Object rootId : roots) {
  601. if (filterIncludingParents(rootId, includedItems)) {
  602. filteredRoots.add(rootId);
  603. addFilteredChildrenRecursively(rootId, includedItems);
  604. }
  605. }
  606. // includedItemIds now contains all the item ids that should be
  607. // included. Filter IndexedContainer based on this
  608. filterOverride = includedItems;
  609. super.doFilterContainer(hasFilters);
  610. filterOverride = null;
  611. return true;
  612. } else {
  613. // Filter by including all items that pass the filter and make items
  614. // with no parent new root items
  615. // Filter IndexedContainer first so getItemIds return the items that
  616. // match
  617. super.doFilterContainer(hasFilters);
  618. LinkedHashSet<Object> filteredItemIds = new LinkedHashSet<Object>(
  619. getItemIds());
  620. for (Object itemId : filteredItemIds) {
  621. Object itemParent = parent.get(itemId);
  622. if (itemParent == null || !filteredItemIds.contains(itemParent)) {
  623. // Parent is not included or this was a root, in both cases
  624. // this should be a filtered root
  625. filteredRoots.add(itemId);
  626. } else {
  627. // Parent is included. Add this to the children list (create
  628. // it first if necessary)
  629. addFilteredChild(itemParent, itemId);
  630. }
  631. }
  632. return true;
  633. }
  634. }
  635. /**
  636. * Adds the given childItemId as a filteredChildren for the parentItemId and
  637. * sets it filteredParent.
  638. *
  639. * @param parentItemId
  640. * @param childItemId
  641. */
  642. private void addFilteredChild(Object parentItemId, Object childItemId) {
  643. LinkedList<Object> parentToChildrenList = filteredChildren
  644. .get(parentItemId);
  645. if (parentToChildrenList == null) {
  646. parentToChildrenList = new LinkedList<Object>();
  647. filteredChildren.put(parentItemId, parentToChildrenList);
  648. }
  649. filteredParent.put(childItemId, parentItemId);
  650. parentToChildrenList.add(childItemId);
  651. }
  652. /**
  653. * Recursively adds all items in the includedItems list to the
  654. * filteredChildren map in the same order as they are in the children map.
  655. * Starts from parentItemId and recurses down as long as child items that
  656. * should be included are found.
  657. *
  658. * @param parentItemId
  659. * The item id to start recurse from. Not added to a
  660. * filteredChildren list
  661. * @param includedItems
  662. * Set containing the item ids for the items that should be
  663. * included in the filteredChildren map
  664. */
  665. private void addFilteredChildrenRecursively(Object parentItemId,
  666. HashSet<Object> includedItems) {
  667. LinkedList<Object> childList = children.get(parentItemId);
  668. if (childList == null) {
  669. return;
  670. }
  671. for (Object childItemId : childList) {
  672. if (includedItems.contains(childItemId)) {
  673. addFilteredChild(parentItemId, childItemId);
  674. addFilteredChildrenRecursively(childItemId, includedItems);
  675. }
  676. }
  677. }
  678. /**
  679. * Scans the itemId and all its children for which items should be included
  680. * when filtering. All items which passes the filters are included.
  681. * Additionally all items that have a child node that should be included are
  682. * also themselves included.
  683. *
  684. * @param itemId
  685. * @param includedItems
  686. * @return true if the itemId should be included in the filtered container.
  687. */
  688. private boolean filterIncludingParents(Object itemId,
  689. HashSet<Object> includedItems) {
  690. boolean toBeIncluded = passesFilters(itemId);
  691. LinkedList<Object> childList = children.get(itemId);
  692. if (childList != null) {
  693. for (Object childItemId : children.get(itemId)) {
  694. toBeIncluded |= filterIncludingParents(childItemId,
  695. includedItems);
  696. }
  697. }
  698. if (toBeIncluded) {
  699. includedItems.add(itemId);
  700. }
  701. return toBeIncluded;
  702. }
  703. private Set<Object> filterOverride = null;
  704. /*
  705. * (non-Javadoc)
  706. *
  707. * @see
  708. * com.vaadin.data.util.IndexedContainer#passesFilters(java.lang.Object)
  709. */
  710. @Override
  711. protected boolean passesFilters(Object itemId) {
  712. if (filterOverride != null) {
  713. return filterOverride.contains(itemId);
  714. } else {
  715. return super.passesFilters(itemId);
  716. }
  717. }
  718. }