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

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