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

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