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

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