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.

ContainerHierarchicalWrapper.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.data.util;
  5. import java.io.Serializable;
  6. import java.util.Arrays;
  7. import java.util.Collection;
  8. import java.util.Collections;
  9. import java.util.Comparator;
  10. import java.util.HashSet;
  11. import java.util.Hashtable;
  12. import java.util.Iterator;
  13. import java.util.LinkedHashSet;
  14. import java.util.LinkedList;
  15. import com.vaadin.data.Container;
  16. import com.vaadin.data.Item;
  17. import com.vaadin.data.Property;
  18. /**
  19. * <p>
  20. * A wrapper class for adding external hierarchy to containers not implementing
  21. * the {@link com.vaadin.data.Container.Hierarchical} interface.
  22. * </p>
  23. *
  24. * <p>
  25. * If the wrapped container is changed directly (that is, not through the
  26. * wrapper), and does not implement Container.ItemSetChangeNotifier and/or
  27. * Container.PropertySetChangeNotifier the hierarchy information must be updated
  28. * with the {@link #updateHierarchicalWrapper()} method.
  29. * </p>
  30. *
  31. * @author Vaadin Ltd.
  32. * @version
  33. * @VERSION@
  34. * @since 3.0
  35. */
  36. @SuppressWarnings("serial")
  37. public class ContainerHierarchicalWrapper implements Container.Hierarchical,
  38. Container.ItemSetChangeNotifier, Container.PropertySetChangeNotifier {
  39. /** The wrapped container */
  40. private final Container container;
  41. /** Set of IDs of those contained Items that can't have children. */
  42. private HashSet<Object> noChildrenAllowed = null;
  43. /** Mapping from Item ID to parent Item ID */
  44. private Hashtable<Object, Object> parent = null;
  45. /** Mapping from Item ID to a list of child IDs */
  46. private Hashtable<Object, LinkedList<Object>> children = null;
  47. /** List that contains all root elements of the container. */
  48. private LinkedHashSet<Object> roots = null;
  49. /** Is the wrapped container hierarchical by itself ? */
  50. private boolean hierarchical;
  51. /**
  52. * A comparator that sorts the listed items before other items. Otherwise,
  53. * the order is undefined.
  54. */
  55. private static class ListedItemsFirstComparator implements
  56. Comparator<Object>, Serializable {
  57. private final Collection<?> itemIds;
  58. private ListedItemsFirstComparator(Collection<?> itemIds) {
  59. this.itemIds = itemIds;
  60. }
  61. @Override
  62. public int compare(Object o1, Object o2) {
  63. if (o1.equals(o2)) {
  64. return 0;
  65. }
  66. for (Object id : itemIds) {
  67. if (id == o1) {
  68. return -1;
  69. } else if (id == o2) {
  70. return 1;
  71. }
  72. }
  73. return 0;
  74. }
  75. };
  76. /**
  77. * Constructs a new hierarchical wrapper for an existing Container. Works
  78. * even if the to-be-wrapped container already implements the
  79. * <code>Container.Hierarchical</code> interface.
  80. *
  81. * @param toBeWrapped
  82. * the container that needs to be accessed hierarchically
  83. * @see #updateHierarchicalWrapper()
  84. */
  85. public ContainerHierarchicalWrapper(Container toBeWrapped) {
  86. container = toBeWrapped;
  87. hierarchical = container instanceof Container.Hierarchical;
  88. // Check arguments
  89. if (container == null) {
  90. throw new NullPointerException("Null can not be wrapped");
  91. }
  92. // Create initial order if needed
  93. if (!hierarchical) {
  94. noChildrenAllowed = new HashSet<Object>();
  95. parent = new Hashtable<Object, Object>();
  96. children = new Hashtable<Object, LinkedList<Object>>();
  97. roots = new LinkedHashSet<Object>(container.getItemIds());
  98. }
  99. updateHierarchicalWrapper();
  100. }
  101. /**
  102. * Updates the wrapper's internal hierarchy data to include all Items in the
  103. * underlying container. If the contents of the wrapped container change
  104. * without the wrapper's knowledge, this method needs to be called to update
  105. * the hierarchy information of the Items.
  106. */
  107. public void updateHierarchicalWrapper() {
  108. if (!hierarchical) {
  109. // Recreate hierarchy and data structures if missing
  110. if (noChildrenAllowed == null || parent == null || children == null
  111. || roots == null) {
  112. noChildrenAllowed = new HashSet<Object>();
  113. parent = new Hashtable<Object, Object>();
  114. children = new Hashtable<Object, LinkedList<Object>>();
  115. roots = new LinkedHashSet<Object>(container.getItemIds());
  116. }
  117. // Check that the hierarchy is up-to-date
  118. else {
  119. // ensure order of root and child lists is same as in wrapped
  120. // container
  121. Collection<?> itemIds = container.getItemIds();
  122. Comparator<Object> basedOnOrderFromWrappedContainer = new ListedItemsFirstComparator(
  123. itemIds);
  124. // Calculate the set of all items in the hierarchy
  125. final HashSet<Object> s = new HashSet<Object>();
  126. s.addAll(parent.keySet());
  127. s.addAll(children.keySet());
  128. s.addAll(roots);
  129. // Remove unnecessary items
  130. for (final Iterator<Object> i = s.iterator(); i.hasNext();) {
  131. final Object id = i.next();
  132. if (!container.containsId(id)) {
  133. removeFromHierarchyWrapper(id);
  134. }
  135. }
  136. // Add all the missing items
  137. final Collection<?> ids = container.getItemIds();
  138. for (final Iterator<?> i = ids.iterator(); i.hasNext();) {
  139. final Object id = i.next();
  140. if (!s.contains(id)) {
  141. addToHierarchyWrapper(id);
  142. s.add(id);
  143. }
  144. }
  145. Object[] array = roots.toArray();
  146. Arrays.sort(array, basedOnOrderFromWrappedContainer);
  147. roots = new LinkedHashSet<Object>();
  148. for (int i = 0; i < array.length; i++) {
  149. roots.add(array[i]);
  150. }
  151. for (Object object : children.keySet()) {
  152. LinkedList<Object> object2 = children.get(object);
  153. Collections.sort(object2, basedOnOrderFromWrappedContainer);
  154. }
  155. }
  156. }
  157. }
  158. /**
  159. * Removes the specified Item from the wrapper's internal hierarchy
  160. * structure.
  161. * <p>
  162. * Note : The Item is not removed from the underlying Container.
  163. * </p>
  164. *
  165. * @param itemId
  166. * the ID of the item to remove from the hierarchy.
  167. */
  168. private void removeFromHierarchyWrapper(Object itemId) {
  169. LinkedList<Object> oprhanedChildren = children.remove(itemId);
  170. if (oprhanedChildren != null) {
  171. for (Object object : oprhanedChildren) {
  172. // make orphaned children root nodes
  173. setParent(object, null);
  174. }
  175. }
  176. roots.remove(itemId);
  177. final Object p = parent.get(itemId);
  178. if (p != null) {
  179. final LinkedList<Object> c = children.get(p);
  180. if (c != null) {
  181. c.remove(itemId);
  182. }
  183. }
  184. parent.remove(itemId);
  185. noChildrenAllowed.remove(itemId);
  186. }
  187. /**
  188. * Adds the specified Item specified to the internal hierarchy structure.
  189. * The new item is added as a root Item. The underlying container is not
  190. * modified.
  191. *
  192. * @param itemId
  193. * the ID of the item to add to the hierarchy.
  194. */
  195. private void addToHierarchyWrapper(Object itemId) {
  196. roots.add(itemId);
  197. }
  198. /*
  199. * Can the specified Item have any children? Don't add a JavaDoc comment
  200. * here, we use the default documentation from implemented interface.
  201. */
  202. @Override
  203. public boolean areChildrenAllowed(Object itemId) {
  204. // If the wrapped container implements the method directly, use it
  205. if (hierarchical) {
  206. return ((Container.Hierarchical) container)
  207. .areChildrenAllowed(itemId);
  208. }
  209. if (noChildrenAllowed.contains(itemId)) {
  210. return false;
  211. }
  212. return containsId(itemId);
  213. }
  214. /*
  215. * Gets the IDs of the children of the specified Item. Don't add a JavaDoc
  216. * comment here, we use the default documentation from implemented
  217. * interface.
  218. */
  219. @Override
  220. public Collection<?> getChildren(Object itemId) {
  221. // If the wrapped container implements the method directly, use it
  222. if (hierarchical) {
  223. return ((Container.Hierarchical) container).getChildren(itemId);
  224. }
  225. final Collection<?> c = children.get(itemId);
  226. if (c == null) {
  227. return null;
  228. }
  229. return Collections.unmodifiableCollection(c);
  230. }
  231. /*
  232. * Gets the ID of the parent of the specified Item. Don't add a JavaDoc
  233. * comment here, we use the default documentation from implemented
  234. * interface.
  235. */
  236. @Override
  237. public Object getParent(Object itemId) {
  238. // If the wrapped container implements the method directly, use it
  239. if (hierarchical) {
  240. return ((Container.Hierarchical) container).getParent(itemId);
  241. }
  242. return parent.get(itemId);
  243. }
  244. /*
  245. * Is the Item corresponding to the given ID a leaf node? Don't add a
  246. * JavaDoc comment here, we use the default documentation from implemented
  247. * interface.
  248. */
  249. @Override
  250. public boolean hasChildren(Object itemId) {
  251. // If the wrapped container implements the method directly, use it
  252. if (hierarchical) {
  253. return ((Container.Hierarchical) container).hasChildren(itemId);
  254. }
  255. LinkedList<Object> list = children.get(itemId);
  256. return (list != null && !list.isEmpty());
  257. }
  258. /*
  259. * Is the Item corresponding to the given ID a root node? Don't add a
  260. * JavaDoc comment here, we use the default documentation from implemented
  261. * interface.
  262. */
  263. @Override
  264. public boolean isRoot(Object itemId) {
  265. // If the wrapped container implements the method directly, use it
  266. if (hierarchical) {
  267. return ((Container.Hierarchical) container).isRoot(itemId);
  268. }
  269. if (parent.containsKey(itemId)) {
  270. return false;
  271. }
  272. return containsId(itemId);
  273. }
  274. /*
  275. * Gets the IDs of the root elements in the container. Don't add a JavaDoc
  276. * comment here, we use the default documentation from implemented
  277. * interface.
  278. */
  279. @Override
  280. public Collection<?> rootItemIds() {
  281. // If the wrapped container implements the method directly, use it
  282. if (hierarchical) {
  283. return ((Container.Hierarchical) container).rootItemIds();
  284. }
  285. return Collections.unmodifiableCollection(roots);
  286. }
  287. /**
  288. * <p>
  289. * Sets the given Item's capability to have children. If the Item identified
  290. * with the itemId already has children and the areChildrenAllowed is false
  291. * this method fails and <code>false</code> is returned; the children must
  292. * be first explicitly removed with
  293. * {@link #setParent(Object itemId, Object newParentId)} or
  294. * {@link com.vaadin.data.Container#removeItem(Object itemId)}.
  295. * </p>
  296. *
  297. * @param itemId
  298. * the ID of the Item in the container whose child capability is
  299. * to be set.
  300. * @param childrenAllowed
  301. * the boolean value specifying if the Item can have children or
  302. * not.
  303. * @return <code>true</code> if the operation succeeded, <code>false</code>
  304. * if not
  305. */
  306. @Override
  307. public boolean setChildrenAllowed(Object itemId, boolean childrenAllowed) {
  308. // If the wrapped container implements the method directly, use it
  309. if (hierarchical) {
  310. return ((Container.Hierarchical) container).setChildrenAllowed(
  311. itemId, childrenAllowed);
  312. }
  313. // Check that the item is in the container
  314. if (!containsId(itemId)) {
  315. return false;
  316. }
  317. // Update status
  318. if (childrenAllowed) {
  319. noChildrenAllowed.remove(itemId);
  320. } else {
  321. noChildrenAllowed.add(itemId);
  322. }
  323. return true;
  324. }
  325. /**
  326. * <p>
  327. * Sets the parent of an Item. The new parent item must exist and be able to
  328. * have children. (<code>canHaveChildren(newParentId) == true</code>). It is
  329. * also possible to detach a node from the hierarchy (and thus make it root)
  330. * by setting the parent <code>null</code>.
  331. * </p>
  332. *
  333. * @param itemId
  334. * the ID of the item to be set as the child of the Item
  335. * identified with newParentId.
  336. * @param newParentId
  337. * the ID of the Item that's to be the new parent of the Item
  338. * identified with itemId.
  339. * @return <code>true</code> if the operation succeeded, <code>false</code>
  340. * if not
  341. */
  342. @Override
  343. public boolean setParent(Object itemId, Object newParentId) {
  344. // If the wrapped container implements the method directly, use it
  345. if (hierarchical) {
  346. return ((Container.Hierarchical) container).setParent(itemId,
  347. newParentId);
  348. }
  349. // Check that the item is in the container
  350. if (!containsId(itemId)) {
  351. return false;
  352. }
  353. // Get the old parent
  354. final Object oldParentId = parent.get(itemId);
  355. // Check if no change is necessary
  356. if ((newParentId == null && oldParentId == null)
  357. || (newParentId != null && newParentId.equals(oldParentId))) {
  358. return true;
  359. }
  360. // Making root
  361. if (newParentId == null) {
  362. // Remove from old parents children list
  363. final LinkedList<Object> l = children.get(oldParentId);
  364. if (l != null) {
  365. l.remove(itemId);
  366. if (l.isEmpty()) {
  367. children.remove(itemId);
  368. }
  369. }
  370. // Add to be a root
  371. roots.add(itemId);
  372. // Update parent
  373. parent.remove(itemId);
  374. return true;
  375. }
  376. // Check that the new parent exists in container and can have
  377. // children
  378. if (!containsId(newParentId) || noChildrenAllowed.contains(newParentId)) {
  379. return false;
  380. }
  381. // Check that setting parent doesn't result to a loop
  382. Object o = newParentId;
  383. while (o != null && !o.equals(itemId)) {
  384. o = parent.get(o);
  385. }
  386. if (o != null) {
  387. return false;
  388. }
  389. // Update parent
  390. parent.put(itemId, newParentId);
  391. LinkedList<Object> pcl = children.get(newParentId);
  392. if (pcl == null) {
  393. pcl = new LinkedList<Object>();
  394. children.put(newParentId, pcl);
  395. }
  396. pcl.add(itemId);
  397. // Remove from old parent or root
  398. if (oldParentId == null) {
  399. roots.remove(itemId);
  400. } else {
  401. final LinkedList<Object> l = children.get(oldParentId);
  402. if (l != null) {
  403. l.remove(itemId);
  404. if (l.isEmpty()) {
  405. children.remove(oldParentId);
  406. }
  407. }
  408. }
  409. return true;
  410. }
  411. /**
  412. * Creates a new Item into the Container, assigns it an automatic ID, and
  413. * adds it to the hierarchy.
  414. *
  415. * @return the autogenerated ID of the new Item or <code>null</code> if the
  416. * operation failed
  417. * @throws UnsupportedOperationException
  418. * if the addItem is not supported.
  419. */
  420. @Override
  421. public Object addItem() throws UnsupportedOperationException {
  422. final Object id = container.addItem();
  423. if (!hierarchical && id != null) {
  424. addToHierarchyWrapper(id);
  425. }
  426. return id;
  427. }
  428. /**
  429. * Adds a new Item by its ID to the underlying container and to the
  430. * hierarchy.
  431. *
  432. * @param itemId
  433. * the ID of the Item to be created.
  434. * @return the added Item or <code>null</code> if the operation failed.
  435. * @throws UnsupportedOperationException
  436. * if the addItem is not supported.
  437. */
  438. @Override
  439. public Item addItem(Object itemId) throws UnsupportedOperationException {
  440. // Null ids are not accepted
  441. if (itemId == null) {
  442. throw new NullPointerException("Container item id can not be null");
  443. }
  444. final Item item = container.addItem(itemId);
  445. if (!hierarchical && item != null) {
  446. addToHierarchyWrapper(itemId);
  447. }
  448. return item;
  449. }
  450. /**
  451. * Removes all items from the underlying container and from the hierarcy.
  452. *
  453. * @return <code>true</code> if the operation succeeded, <code>false</code>
  454. * if not
  455. * @throws UnsupportedOperationException
  456. * if the removeAllItems is not supported.
  457. */
  458. @Override
  459. public boolean removeAllItems() throws UnsupportedOperationException {
  460. final boolean success = container.removeAllItems();
  461. if (!hierarchical && success) {
  462. roots.clear();
  463. parent.clear();
  464. children.clear();
  465. noChildrenAllowed.clear();
  466. }
  467. return success;
  468. }
  469. /**
  470. * Removes an Item specified by the itemId from the underlying container and
  471. * from the hierarchy.
  472. *
  473. * @param itemId
  474. * the ID of the Item to be removed.
  475. * @return <code>true</code> if the operation succeeded, <code>false</code>
  476. * if not
  477. * @throws UnsupportedOperationException
  478. * if the removeItem is not supported.
  479. */
  480. @Override
  481. public boolean removeItem(Object itemId)
  482. throws UnsupportedOperationException {
  483. final boolean success = container.removeItem(itemId);
  484. if (!hierarchical && success) {
  485. removeFromHierarchyWrapper(itemId);
  486. }
  487. return success;
  488. }
  489. /**
  490. * Removes the Item identified by given itemId and all its children.
  491. *
  492. * @see #removeItem(Object)
  493. * @param itemId
  494. * the identifier of the Item to be removed
  495. * @return true if the operation succeeded
  496. */
  497. public boolean removeItemRecursively(Object itemId) {
  498. return HierarchicalContainer.removeItemRecursively(this, itemId);
  499. }
  500. /**
  501. * Adds a new Property to all Items in the Container.
  502. *
  503. * @param propertyId
  504. * the ID of the new Property.
  505. * @param type
  506. * the Data type of the new Property.
  507. * @param defaultValue
  508. * the value all created Properties are initialized to.
  509. * @return <code>true</code> if the operation succeeded, <code>false</code>
  510. * if not
  511. * @throws UnsupportedOperationException
  512. * if the addContainerProperty is not supported.
  513. */
  514. @Override
  515. public boolean addContainerProperty(Object propertyId, Class<?> type,
  516. Object defaultValue) throws UnsupportedOperationException {
  517. return container.addContainerProperty(propertyId, type, defaultValue);
  518. }
  519. /**
  520. * Removes the specified Property from the underlying container and from the
  521. * hierarchy.
  522. * <p>
  523. * Note : The Property will be removed from all Items in the Container.
  524. * </p>
  525. *
  526. * @param propertyId
  527. * the ID of the Property to remove.
  528. * @return <code>true</code> if the operation succeeded, <code>false</code>
  529. * if not
  530. * @throws UnsupportedOperationException
  531. * if the removeContainerProperty is not supported.
  532. */
  533. @Override
  534. public boolean removeContainerProperty(Object propertyId)
  535. throws UnsupportedOperationException {
  536. return container.removeContainerProperty(propertyId);
  537. }
  538. /*
  539. * Does the container contain the specified Item? Don't add a JavaDoc
  540. * comment here, we use the default documentation from implemented
  541. * interface.
  542. */
  543. @Override
  544. public boolean containsId(Object itemId) {
  545. return container.containsId(itemId);
  546. }
  547. /*
  548. * Gets the specified Item from the container. Don't add a JavaDoc comment
  549. * here, we use the default documentation from implemented interface.
  550. */
  551. @Override
  552. public Item getItem(Object itemId) {
  553. return container.getItem(itemId);
  554. }
  555. /*
  556. * Gets the ID's of all Items stored in the Container Don't add a JavaDoc
  557. * comment here, we use the default documentation from implemented
  558. * interface.
  559. */
  560. @Override
  561. public Collection<?> getItemIds() {
  562. return container.getItemIds();
  563. }
  564. /*
  565. * Gets the Property identified by the given itemId and propertyId from the
  566. * Container Don't add a JavaDoc comment here, we use the default
  567. * documentation from implemented interface.
  568. */
  569. @Override
  570. public Property<?> getContainerProperty(Object itemId, Object propertyId) {
  571. return container.getContainerProperty(itemId, propertyId);
  572. }
  573. /*
  574. * Gets the ID's of all Properties stored in the Container Don't add a
  575. * JavaDoc comment here, we use the default documentation from implemented
  576. * interface.
  577. */
  578. @Override
  579. public Collection<?> getContainerPropertyIds() {
  580. return container.getContainerPropertyIds();
  581. }
  582. /*
  583. * Gets the data type of all Properties identified by the given Property ID.
  584. * Don't add a JavaDoc comment here, we use the default documentation from
  585. * implemented interface.
  586. */
  587. @Override
  588. public Class<?> getType(Object propertyId) {
  589. return container.getType(propertyId);
  590. }
  591. /*
  592. * Gets the number of Items in the Container. Don't add a JavaDoc comment
  593. * here, we use the default documentation from implemented interface.
  594. */
  595. @Override
  596. public int size() {
  597. return container.size();
  598. }
  599. /*
  600. * Registers a new Item set change listener for this Container. Don't add a
  601. * JavaDoc comment here, we use the default documentation from implemented
  602. * interface.
  603. */
  604. @Override
  605. public void addListener(Container.ItemSetChangeListener listener) {
  606. if (container instanceof Container.ItemSetChangeNotifier) {
  607. ((Container.ItemSetChangeNotifier) container)
  608. .addListener(new PiggybackListener(listener));
  609. }
  610. }
  611. /*
  612. * Removes a Item set change listener from the object. Don't add a JavaDoc
  613. * comment here, we use the default documentation from implemented
  614. * interface.
  615. */
  616. @Override
  617. public void removeListener(Container.ItemSetChangeListener listener) {
  618. if (container instanceof Container.ItemSetChangeNotifier) {
  619. ((Container.ItemSetChangeNotifier) container)
  620. .removeListener(new PiggybackListener(listener));
  621. }
  622. }
  623. /*
  624. * Registers a new Property set change listener for this Container. Don't
  625. * add a JavaDoc comment here, we use the default documentation from
  626. * implemented interface.
  627. */
  628. @Override
  629. public void addListener(Container.PropertySetChangeListener listener) {
  630. if (container instanceof Container.PropertySetChangeNotifier) {
  631. ((Container.PropertySetChangeNotifier) container)
  632. .addListener(new PiggybackListener(listener));
  633. }
  634. }
  635. /*
  636. * Removes a Property set change listener from the object. Don't add a
  637. * JavaDoc comment here, we use the default documentation from implemented
  638. * interface.
  639. */
  640. @Override
  641. public void removeListener(Container.PropertySetChangeListener listener) {
  642. if (container instanceof Container.PropertySetChangeNotifier) {
  643. ((Container.PropertySetChangeNotifier) container)
  644. .removeListener(new PiggybackListener(listener));
  645. }
  646. }
  647. /**
  648. * This listener 'piggybacks' on the real listener in order to update the
  649. * wrapper when needed. It proxies equals() and hashCode() to the real
  650. * listener so that the correct listener gets removed.
  651. *
  652. */
  653. private class PiggybackListener implements
  654. Container.PropertySetChangeListener,
  655. Container.ItemSetChangeListener {
  656. Object listener;
  657. public PiggybackListener(Object realListener) {
  658. listener = realListener;
  659. }
  660. @Override
  661. public void containerItemSetChange(ItemSetChangeEvent event) {
  662. updateHierarchicalWrapper();
  663. ((Container.ItemSetChangeListener) listener)
  664. .containerItemSetChange(event);
  665. }
  666. @Override
  667. public void containerPropertySetChange(PropertySetChangeEvent event) {
  668. updateHierarchicalWrapper();
  669. ((Container.PropertySetChangeListener) listener)
  670. .containerPropertySetChange(event);
  671. }
  672. @Override
  673. public boolean equals(Object obj) {
  674. return obj == listener || (obj != null && obj.equals(listener));
  675. }
  676. @Override
  677. public int hashCode() {
  678. return listener.hashCode();
  679. }
  680. }
  681. }