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.

ContainerOrderedWrapper.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.data.util;
  5. import java.util.Collection;
  6. import java.util.Hashtable;
  7. import java.util.Iterator;
  8. import java.util.LinkedList;
  9. import com.vaadin.data.Container;
  10. import com.vaadin.data.Item;
  11. import com.vaadin.data.Property;
  12. /**
  13. * <p>
  14. * A wrapper class for adding external ordering to containers not implementing
  15. * the {@link com.vaadin.data.Container.Ordered} interface.
  16. * </p>
  17. *
  18. * <p>
  19. * If the wrapped container is changed directly (that is, not through the
  20. * wrapper), and does not implement Container.ItemSetChangeNotifier and/or
  21. * Container.PropertySetChangeNotifier the hierarchy information must be updated
  22. * with the {@link #updateOrderWrapper()} method.
  23. * </p>
  24. *
  25. * @author IT Mill Ltd.
  26. * @version
  27. * @VERSION@
  28. * @since 3.0
  29. */
  30. @SuppressWarnings("serial")
  31. public class ContainerOrderedWrapper implements Container.Ordered,
  32. Container.ItemSetChangeNotifier, Container.PropertySetChangeNotifier {
  33. /**
  34. * The wrapped container
  35. */
  36. private final Container container;
  37. /**
  38. * Ordering information, ie. the mapping from Item ID to the next item ID
  39. */
  40. private Hashtable next;
  41. /**
  42. * Reverse ordering information for convenience and performance reasons.
  43. */
  44. private Hashtable prev;
  45. /**
  46. * ID of the first Item in the container.
  47. */
  48. private Object first;
  49. /**
  50. * ID of the last Item in the container.
  51. */
  52. private Object last;
  53. /**
  54. * Is the wrapped container ordered by itself, ie. does it implement the
  55. * Container.Ordered interface by itself? If it does, this class will use
  56. * the methods of the underlying container directly.
  57. */
  58. private boolean ordered = false;
  59. /**
  60. * Constructs a new ordered wrapper for an existing Container. Works even if
  61. * the to-be-wrapped container already implements the Container.Ordered
  62. * interface.
  63. *
  64. * @param toBeWrapped
  65. * the container whose contents need to be ordered.
  66. */
  67. public ContainerOrderedWrapper(Container toBeWrapped) {
  68. container = toBeWrapped;
  69. ordered = container instanceof Container.Ordered;
  70. // Checks arguments
  71. if (container == null) {
  72. throw new NullPointerException("Null can not be wrapped");
  73. }
  74. // Creates initial order if needed
  75. updateOrderWrapper();
  76. }
  77. /**
  78. * Removes the specified Item from the wrapper's internal hierarchy
  79. * structure.
  80. * <p>
  81. * Note : The Item is not removed from the underlying Container.
  82. * </p>
  83. *
  84. * @param id
  85. * the ID of the Item to be removed from the ordering.
  86. */
  87. private void removeFromOrderWrapper(Object id) {
  88. if (id != null) {
  89. final Object pid = prev.get(id);
  90. final Object nid = next.get(id);
  91. if (first.equals(id)) {
  92. first = nid;
  93. }
  94. if (last.equals(id)) {
  95. first = pid;
  96. }
  97. if (nid != null) {
  98. prev.put(nid, pid);
  99. }
  100. if (pid != null) {
  101. next.put(pid, nid);
  102. }
  103. next.remove(id);
  104. prev.remove(id);
  105. }
  106. }
  107. /**
  108. * Registers the specified Item to the last position in the wrapper's
  109. * internal ordering. The underlying container is not modified.
  110. *
  111. * @param id
  112. * the ID of the Item to be added to the ordering.
  113. */
  114. private void addToOrderWrapper(Object id) {
  115. // Adds the if to tail
  116. if (last != null) {
  117. next.put(last, id);
  118. prev.put(id, last);
  119. last = id;
  120. } else {
  121. first = last = id;
  122. }
  123. }
  124. /**
  125. * Registers the specified Item after the specified itemId in the wrapper's
  126. * internal ordering. The underlying container is not modified. Given item
  127. * id must be in the container, or must be null.
  128. *
  129. * @param id
  130. * the ID of the Item to be added to the ordering.
  131. * @param previousItemId
  132. * the Id of the previous item.
  133. */
  134. private void addToOrderWrapper(Object id, Object previousItemId) {
  135. if (last == previousItemId || last == null) {
  136. addToOrderWrapper(id);
  137. } else {
  138. if (previousItemId == null) {
  139. next.put(id, first);
  140. prev.put(first, id);
  141. first = id;
  142. } else {
  143. prev.put(id, previousItemId);
  144. next.put(id, next.get(previousItemId));
  145. prev.put(next.get(previousItemId), id);
  146. next.put(previousItemId, id);
  147. }
  148. }
  149. }
  150. /**
  151. * Updates the wrapper's internal ordering information to include all Items
  152. * in the underlying container.
  153. * <p>
  154. * Note : If the contents of the wrapped container change without the
  155. * wrapper's knowledge, this method needs to be called to update the
  156. * ordering information of the Items.
  157. * </p>
  158. */
  159. public void updateOrderWrapper() {
  160. if (!ordered) {
  161. final Collection ids = container.getItemIds();
  162. // Recreates ordering if some parts of it are missing
  163. if (next == null || first == null || last == null || prev != null) {
  164. first = null;
  165. last = null;
  166. next = new Hashtable();
  167. prev = new Hashtable();
  168. }
  169. // Filter out all the missing items
  170. final LinkedList l = new LinkedList(next.keySet());
  171. for (final Iterator i = l.iterator(); i.hasNext();) {
  172. final Object id = i.next();
  173. if (!container.containsId(id)) {
  174. removeFromOrderWrapper(id);
  175. }
  176. }
  177. // Adds missing items
  178. for (final Iterator i = ids.iterator(); i.hasNext();) {
  179. final Object id = i.next();
  180. if (!next.containsKey(id)) {
  181. addToOrderWrapper(id);
  182. }
  183. }
  184. }
  185. }
  186. /*
  187. * Gets the first item stored in the ordered container Don't add a JavaDoc
  188. * comment here, we use the default documentation from implemented
  189. * interface.
  190. */
  191. public Object firstItemId() {
  192. if (ordered) {
  193. return ((Container.Ordered) container).firstItemId();
  194. }
  195. return first;
  196. }
  197. /*
  198. * Tests if the given item is the first item in the container Don't add a
  199. * JavaDoc comment here, we use the default documentation from implemented
  200. * interface.
  201. */
  202. public boolean isFirstId(Object itemId) {
  203. if (ordered) {
  204. return ((Container.Ordered) container).isFirstId(itemId);
  205. }
  206. return first != null && first.equals(itemId);
  207. }
  208. /*
  209. * Tests if the given item is the last item in the container Don't add a
  210. * JavaDoc comment here, we use the default documentation from implemented
  211. * interface.
  212. */
  213. public boolean isLastId(Object itemId) {
  214. if (ordered) {
  215. return ((Container.Ordered) container).isLastId(itemId);
  216. }
  217. return last != null && last.equals(itemId);
  218. }
  219. /*
  220. * Gets the last item stored in the ordered container Don't add a JavaDoc
  221. * comment here, we use the default documentation from implemented
  222. * interface.
  223. */
  224. public Object lastItemId() {
  225. if (ordered) {
  226. return ((Container.Ordered) container).lastItemId();
  227. }
  228. return last;
  229. }
  230. /*
  231. * Gets the item that is next from the specified item. Don't add a JavaDoc
  232. * comment here, we use the default documentation from implemented
  233. * interface.
  234. */
  235. public Object nextItemId(Object itemId) {
  236. if (ordered) {
  237. return ((Container.Ordered) container).nextItemId(itemId);
  238. }
  239. if (itemId == null) {
  240. return null;
  241. }
  242. return next.get(itemId);
  243. }
  244. /*
  245. * Gets the item that is previous from the specified item. Don't add a
  246. * JavaDoc comment here, we use the default documentation from implemented
  247. * interface.
  248. */
  249. public Object prevItemId(Object itemId) {
  250. if (ordered) {
  251. return ((Container.Ordered) container).prevItemId(itemId);
  252. }
  253. if (itemId == null) {
  254. return null;
  255. }
  256. return prev.get(itemId);
  257. }
  258. /**
  259. * Registers a new Property to all Items in the Container.
  260. *
  261. * @param propertyId
  262. * the ID of the new Property.
  263. * @param type
  264. * the Data type of the new Property.
  265. * @param defaultValue
  266. * the value all created Properties are initialized to.
  267. * @return <code>true</code> if the operation succeeded, <code>false</code>
  268. * if not
  269. */
  270. public boolean addContainerProperty(Object propertyId, Class type,
  271. Object defaultValue) throws UnsupportedOperationException {
  272. return container.addContainerProperty(propertyId, type, defaultValue);
  273. }
  274. /**
  275. * Creates a new Item into the Container, assigns it an automatic ID, and
  276. * adds it to the ordering.
  277. *
  278. * @return the autogenerated ID of the new Item or <code>null</code> if the
  279. * operation failed
  280. * @throws UnsupportedOperationException
  281. * if the addItem is not supported.
  282. */
  283. public Object addItem() throws UnsupportedOperationException {
  284. final Object id = container.addItem();
  285. if (!ordered && id != null) {
  286. addToOrderWrapper(id);
  287. }
  288. return id;
  289. }
  290. /**
  291. * Registers a new Item by its ID to the underlying container and to the
  292. * ordering.
  293. *
  294. * @param itemId
  295. * the ID of the Item to be created.
  296. * @return the added Item or <code>null</code> if the operation failed
  297. * @throws UnsupportedOperationException
  298. * if the addItem is not supported.
  299. */
  300. public Item addItem(Object itemId) throws UnsupportedOperationException {
  301. final Item item = container.addItem(itemId);
  302. if (!ordered && item != null) {
  303. addToOrderWrapper(itemId);
  304. }
  305. return item;
  306. }
  307. /**
  308. * Removes all items from the underlying container and from the ordering.
  309. *
  310. * @return <code>true</code> if the operation succeeded, otherwise
  311. * <code>false</code>
  312. * @throws UnsupportedOperationException
  313. * if the removeAllItems is not supported.
  314. */
  315. public boolean removeAllItems() throws UnsupportedOperationException {
  316. final boolean success = container.removeAllItems();
  317. if (!ordered && success) {
  318. first = last = null;
  319. next.clear();
  320. prev.clear();
  321. }
  322. return success;
  323. }
  324. /**
  325. * Removes an Item specified by the itemId from the underlying container and
  326. * from the ordering.
  327. *
  328. * @param itemId
  329. * the ID of the Item to be removed.
  330. * @return <code>true</code> if the operation succeeded, <code>false</code>
  331. * if not
  332. * @throws UnsupportedOperationException
  333. * if the removeItem is not supported.
  334. */
  335. public boolean removeItem(Object itemId)
  336. throws UnsupportedOperationException {
  337. final boolean success = container.removeItem(itemId);
  338. if (!ordered && success) {
  339. removeFromOrderWrapper(itemId);
  340. }
  341. return success;
  342. }
  343. /**
  344. * Removes the specified Property from the underlying container and from the
  345. * ordering.
  346. * <p>
  347. * Note : The Property will be removed from all the Items in the Container.
  348. * </p>
  349. *
  350. * @param propertyId
  351. * the ID of the Property to remove.
  352. * @return <code>true</code> if the operation succeeded, <code>false</code>
  353. * if not
  354. * @throws UnsupportedOperationException
  355. * if the removeContainerProperty is not supported.
  356. */
  357. public boolean removeContainerProperty(Object propertyId)
  358. throws UnsupportedOperationException {
  359. return container.removeContainerProperty(propertyId);
  360. }
  361. /*
  362. * Does the container contain the specified Item? Don't add a JavaDoc
  363. * comment here, we use the default documentation from implemented
  364. * interface.
  365. */
  366. public boolean containsId(Object itemId) {
  367. return container.containsId(itemId);
  368. }
  369. /*
  370. * Gets the specified Item from the container. Don't add a JavaDoc comment
  371. * here, we use the default documentation from implemented interface.
  372. */
  373. public Item getItem(Object itemId) {
  374. return container.getItem(itemId);
  375. }
  376. /*
  377. * Gets the ID's of all Items stored in the Container Don't add a JavaDoc
  378. * comment here, we use the default documentation from implemented
  379. * interface.
  380. */
  381. public Collection getItemIds() {
  382. return container.getItemIds();
  383. }
  384. /*
  385. * Gets the Property identified by the given itemId and propertyId from the
  386. * Container Don't add a JavaDoc comment here, we use the default
  387. * documentation from implemented interface.
  388. */
  389. public Property getContainerProperty(Object itemId, Object propertyId) {
  390. return container.getContainerProperty(itemId, propertyId);
  391. }
  392. /*
  393. * Gets the ID's of all Properties stored in the Container Don't add a
  394. * JavaDoc comment here, we use the default documentation from implemented
  395. * interface.
  396. */
  397. public Collection getContainerPropertyIds() {
  398. return container.getContainerPropertyIds();
  399. }
  400. /*
  401. * Gets the data type of all Properties identified by the given Property ID.
  402. * Don't add a JavaDoc comment here, we use the default documentation from
  403. * implemented interface.
  404. */
  405. public Class getType(Object propertyId) {
  406. return container.getType(propertyId);
  407. }
  408. /*
  409. * Gets the number of Items in the Container. Don't add a JavaDoc comment
  410. * here, we use the default documentation from implemented interface.
  411. */
  412. public int size() {
  413. return container.size();
  414. }
  415. /*
  416. * Registers a new Item set change listener for this Container. Don't add a
  417. * JavaDoc comment here, we use the default documentation from implemented
  418. * interface.
  419. */
  420. public void addListener(Container.ItemSetChangeListener listener) {
  421. if (container instanceof Container.ItemSetChangeNotifier) {
  422. ((Container.ItemSetChangeNotifier) container)
  423. .addListener(new PiggybackListener(listener));
  424. }
  425. }
  426. /*
  427. * Removes a Item set change listener from the object. Don't add a JavaDoc
  428. * comment here, we use the default documentation from implemented
  429. * interface.
  430. */
  431. public void removeListener(Container.ItemSetChangeListener listener) {
  432. if (container instanceof Container.ItemSetChangeNotifier) {
  433. ((Container.ItemSetChangeNotifier) container)
  434. .removeListener(new PiggybackListener(listener));
  435. }
  436. }
  437. /*
  438. * Registers a new Property set change listener for this Container. Don't
  439. * add a JavaDoc comment here, we use the default documentation from
  440. * implemented interface.
  441. */
  442. public void addListener(Container.PropertySetChangeListener listener) {
  443. if (container instanceof Container.PropertySetChangeNotifier) {
  444. ((Container.PropertySetChangeNotifier) container)
  445. .addListener(new PiggybackListener(listener));
  446. }
  447. }
  448. /*
  449. * Removes a Property set change listener from the object. Don't add a
  450. * JavaDoc comment here, we use the default documentation from implemented
  451. * interface.
  452. */
  453. public void removeListener(Container.PropertySetChangeListener listener) {
  454. if (container instanceof Container.PropertySetChangeNotifier) {
  455. ((Container.PropertySetChangeNotifier) container)
  456. .removeListener(new PiggybackListener(listener));
  457. }
  458. }
  459. /*
  460. * (non-Javadoc)
  461. *
  462. * @see
  463. * com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
  464. * java.lang.Object)
  465. */
  466. public Item addItemAfter(Object previousItemId, Object newItemId)
  467. throws UnsupportedOperationException {
  468. // If the previous item is not in the container, fail
  469. if (previousItemId != null && !containsId(previousItemId)) {
  470. return null;
  471. }
  472. // Adds the item to container
  473. final Item item = container.addItem(newItemId);
  474. // Puts the new item to its correct place
  475. if (!ordered && item != null) {
  476. addToOrderWrapper(newItemId, previousItemId);
  477. }
  478. return item;
  479. }
  480. /*
  481. * (non-Javadoc)
  482. *
  483. * @see
  484. * com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
  485. */
  486. public Object addItemAfter(Object previousItemId)
  487. throws UnsupportedOperationException {
  488. // If the previous item is not in the container, fail
  489. if (previousItemId != null && !containsId(previousItemId)) {
  490. return null;
  491. }
  492. // Adds the item to container
  493. final Object id = container.addItem();
  494. // Puts the new item to its correct place
  495. if (!ordered && id != null) {
  496. addToOrderWrapper(id, previousItemId);
  497. }
  498. return id;
  499. }
  500. /**
  501. * This listener 'piggybacks' on the real listener in order to update the
  502. * wrapper when needed. It proxies equals() and hashCode() to the real
  503. * listener so that the correct listener gets removed.
  504. *
  505. */
  506. private class PiggybackListener implements
  507. Container.PropertySetChangeListener,
  508. Container.ItemSetChangeListener {
  509. Object listener;
  510. public PiggybackListener(Object realListener) {
  511. listener = realListener;
  512. }
  513. public void containerItemSetChange(ItemSetChangeEvent event) {
  514. updateOrderWrapper();
  515. ((Container.ItemSetChangeListener) listener)
  516. .containerItemSetChange(event);
  517. }
  518. public void containerPropertySetChange(PropertySetChangeEvent event) {
  519. updateOrderWrapper();
  520. ((Container.PropertySetChangeListener) listener)
  521. .containerPropertySetChange(event);
  522. }
  523. @Override
  524. public boolean equals(Object obj) {
  525. return obj == listener || (obj != null && obj.equals(listener));
  526. }
  527. @Override
  528. public int hashCode() {
  529. return listener.hashCode();
  530. }
  531. }
  532. }