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

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