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

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