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.

AbstractInMemoryContainer.java 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.data.util;
  5. import java.util.Collection;
  6. import java.util.Collections;
  7. import java.util.HashSet;
  8. import java.util.Iterator;
  9. import java.util.LinkedList;
  10. import java.util.List;
  11. import java.util.Set;
  12. import com.vaadin.data.Container;
  13. import com.vaadin.data.Container.ItemSetChangeNotifier;
  14. import com.vaadin.data.Item;
  15. import com.vaadin.data.util.filter.SimpleStringFilter;
  16. import com.vaadin.data.util.filter.UnsupportedFilterException;
  17. /**
  18. * Abstract {@link Container} class that handles common functionality for
  19. * in-memory containers. Concrete in-memory container classes can either inherit
  20. * this class, inherit {@link AbstractContainer}, or implement the
  21. * {@link Container} interface directly.
  22. *
  23. * Adding and removing items (if desired) must be implemented in subclasses by
  24. * overriding the appropriate add*Item() and remove*Item() and removeAllItems()
  25. * methods, calling the corresponding
  26. * {@link #internalAddItemAfter(Object, Object, Item)},
  27. * {@link #internalAddItemAt(int, Object, Item)},
  28. * {@link #internalAddItemAtEnd(Object, Item, boolean)},
  29. * {@link #internalRemoveItem(Object)} and {@link #internalRemoveAllItems()}
  30. * methods.
  31. *
  32. * By default, adding and removing container properties is not supported, and
  33. * subclasses need to implement {@link #getContainerPropertyIds()}. Optionally,
  34. * subclasses can override {@link #addContainerProperty(Object, Class, Object)}
  35. * and {@link #removeContainerProperty(Object)} to implement them.
  36. *
  37. * Features:
  38. * <ul>
  39. * <li> {@link Container.Ordered}
  40. * <li> {@link Container.Indexed}
  41. * <li> {@link Filterable} and {@link SimpleFilterable} (internal implementation,
  42. * does not implement the interface directly)
  43. * <li> {@link Sortable} (internal implementation, does not implement the
  44. * interface directly)
  45. * </ul>
  46. *
  47. * To implement {@link Sortable}, subclasses need to implement
  48. * {@link #getSortablePropertyIds()} and call the superclass method
  49. * {@link #sortContainer(Object[], boolean[])} in the method
  50. * <code>sort(Object[], boolean[])</code>.
  51. *
  52. * To implement {@link Filterable}, subclasses need to implement the methods
  53. * {@link Filterable#addContainerFilter(com.vaadin.data.Container.Filter)}
  54. * (calling {@link #addFilter(Filter)}),
  55. * {@link Filterable#removeAllContainerFilters()} (calling
  56. * {@link #removeAllFilters()}) and
  57. * {@link Filterable#removeContainerFilter(com.vaadin.data.Container.Filter)}
  58. * (calling {@link #removeFilter(com.vaadin.data.Container.Filter)}).
  59. *
  60. * To implement {@link SimpleFilterable}, subclasses also need to implement the
  61. * methods
  62. * {@link SimpleFilterable#addContainerFilter(Object, String, boolean, boolean)}
  63. * and {@link SimpleFilterable#removeContainerFilters(Object)} calling
  64. * {@link #addFilter(com.vaadin.data.Container.Filter)} and
  65. * {@link #removeFilters(Object)} respectively.
  66. *
  67. * @param <ITEMIDTYPE>
  68. * the class of item identifiers in the container, use Object if can
  69. * be any class
  70. * @param <PROPERTYIDCLASS>
  71. * the class of property identifiers for the items in the container,
  72. * use Object if can be any class
  73. * @param <ITEMCLASS>
  74. * the (base) class of the Item instances in the container, use
  75. * {@link Item} if unknown
  76. *
  77. * @since 6.6
  78. */
  79. public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITEMCLASS extends Item>
  80. extends AbstractContainer implements ItemSetChangeNotifier,
  81. Container.Indexed {
  82. /**
  83. * An ordered {@link List} of all item identifiers in the container,
  84. * including those that have been filtered out.
  85. *
  86. * Must not be null.
  87. */
  88. private List<ITEMIDTYPE> allItemIds;
  89. /**
  90. * An ordered {@link List} of item identifiers in the container after
  91. * filtering, excluding those that have been filtered out.
  92. *
  93. * This is what the external API of the {@link Container} interface and its
  94. * subinterfaces shows (e.g. {@link #size()}, {@link #nextItemId(Object)}).
  95. *
  96. * If null, the full item id list is used instead.
  97. */
  98. private List<ITEMIDTYPE> filteredItemIds;
  99. /**
  100. * Filters that are applied to the container to limit the items visible in
  101. * it
  102. */
  103. private Set<Filter> filters = new HashSet<Filter>();
  104. /**
  105. * The item sorter which is used for sorting the container.
  106. */
  107. private ItemSorter itemSorter = new DefaultItemSorter();
  108. // Constructors
  109. /**
  110. * Constructor for an abstract in-memory container.
  111. */
  112. protected AbstractInMemoryContainer() {
  113. setAllItemIds(new ListSet<ITEMIDTYPE>());
  114. }
  115. // Container interface methods with more specific return class
  116. // default implementation, can be overridden
  117. public ITEMCLASS getItem(Object itemId) {
  118. if (containsId(itemId)) {
  119. return getUnfilteredItem(itemId);
  120. } else {
  121. return null;
  122. }
  123. }
  124. /**
  125. * Get an item even if filtered out.
  126. *
  127. * For internal use only.
  128. *
  129. * @param itemId
  130. * @return
  131. */
  132. protected abstract ITEMCLASS getUnfilteredItem(Object itemId);
  133. // cannot override getContainerPropertyIds() and getItemIds(): if subclass
  134. // uses Object as ITEMIDCLASS or PROPERTYIDCLASS, Collection<Object> cannot
  135. // be cast to Collection<MyInterface>
  136. // public abstract Collection<PROPERTYIDCLASS> getContainerPropertyIds();
  137. // public abstract Collection<ITEMIDCLASS> getItemIds();
  138. // Container interface method implementations
  139. public int size() {
  140. return getVisibleItemIds().size();
  141. }
  142. public boolean containsId(Object itemId) {
  143. // only look at visible items after filtering
  144. if (itemId == null) {
  145. return false;
  146. } else {
  147. return getVisibleItemIds().contains(itemId);
  148. }
  149. }
  150. public Collection<?> getItemIds() {
  151. return Collections.unmodifiableCollection(getVisibleItemIds());
  152. }
  153. // Container.Ordered
  154. public ITEMIDTYPE nextItemId(Object itemId) {
  155. int index = indexOfId(itemId);
  156. if (index >= 0 && index < size() - 1) {
  157. return getIdByIndex(index + 1);
  158. } else {
  159. // out of bounds
  160. return null;
  161. }
  162. }
  163. public ITEMIDTYPE prevItemId(Object itemId) {
  164. int index = indexOfId(itemId);
  165. if (index > 0) {
  166. return getIdByIndex(index - 1);
  167. } else {
  168. // out of bounds
  169. return null;
  170. }
  171. }
  172. public ITEMIDTYPE firstItemId() {
  173. if (size() > 0) {
  174. return getIdByIndex(0);
  175. } else {
  176. return null;
  177. }
  178. }
  179. public ITEMIDTYPE lastItemId() {
  180. if (size() > 0) {
  181. return getIdByIndex(size() - 1);
  182. } else {
  183. return null;
  184. }
  185. }
  186. public boolean isFirstId(Object itemId) {
  187. if (itemId == null) {
  188. return false;
  189. }
  190. return itemId.equals(firstItemId());
  191. }
  192. public boolean isLastId(Object itemId) {
  193. if (itemId == null) {
  194. return false;
  195. }
  196. return itemId.equals(lastItemId());
  197. }
  198. // Container.Indexed
  199. public ITEMIDTYPE getIdByIndex(int index) {
  200. return getVisibleItemIds().get(index);
  201. }
  202. public int indexOfId(Object itemId) {
  203. return getVisibleItemIds().indexOf(itemId);
  204. }
  205. // methods that are unsupported by default, override to support
  206. public Object addItemAt(int index) throws UnsupportedOperationException {
  207. throw new UnsupportedOperationException(
  208. "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc.");
  209. }
  210. public Item addItemAt(int index, Object newItemId)
  211. throws UnsupportedOperationException {
  212. throw new UnsupportedOperationException(
  213. "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc.");
  214. }
  215. public Object addItemAfter(Object previousItemId)
  216. throws UnsupportedOperationException {
  217. throw new UnsupportedOperationException(
  218. "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc.");
  219. }
  220. public Item addItemAfter(Object previousItemId, Object newItemId)
  221. throws UnsupportedOperationException {
  222. throw new UnsupportedOperationException(
  223. "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc.");
  224. }
  225. public Item addItem(Object itemId) throws UnsupportedOperationException {
  226. throw new UnsupportedOperationException(
  227. "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc.");
  228. }
  229. public Object addItem() throws UnsupportedOperationException {
  230. throw new UnsupportedOperationException(
  231. "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc.");
  232. }
  233. public boolean removeItem(Object itemId)
  234. throws UnsupportedOperationException {
  235. throw new UnsupportedOperationException(
  236. "Removing items not supported. Override the removeItem() method if required as specified in AbstractInMemoryContainer javadoc.");
  237. }
  238. public boolean removeAllItems() throws UnsupportedOperationException {
  239. throw new UnsupportedOperationException(
  240. "Removing items not supported. Override the removeAllItems() method if required as specified in AbstractInMemoryContainer javadoc.");
  241. }
  242. public boolean addContainerProperty(Object propertyId, Class<?> type,
  243. Object defaultValue) throws UnsupportedOperationException {
  244. throw new UnsupportedOperationException(
  245. "Adding container properties not supported. Override the addContainerProperty() method if required.");
  246. }
  247. public boolean removeContainerProperty(Object propertyId)
  248. throws UnsupportedOperationException {
  249. throw new UnsupportedOperationException(
  250. "Removing container properties not supported. Override the addContainerProperty() method if required.");
  251. }
  252. // ItemSetChangeNotifier
  253. @Override
  254. public void addListener(Container.ItemSetChangeListener listener) {
  255. super.addListener(listener);
  256. }
  257. @Override
  258. public void removeListener(Container.ItemSetChangeListener listener) {
  259. super.removeListener(listener);
  260. }
  261. // internal methods
  262. // Filtering support
  263. /**
  264. * Filter the view to recreate the visible item list from the unfiltered
  265. * items, and send a notification if the set of visible items changed in any
  266. * way.
  267. */
  268. protected void filterAll() {
  269. if (doFilterContainer(!getFilters().isEmpty())) {
  270. fireItemSetChange();
  271. }
  272. }
  273. /**
  274. * Filters the data in the container and updates internal data structures.
  275. * This method should reset any internal data structures and then repopulate
  276. * them so {@link #getItemIds()} and other methods only return the filtered
  277. * items.
  278. *
  279. * @param hasFilters
  280. * true if filters has been set for the container, false
  281. * otherwise
  282. * @return true if the item set has changed as a result of the filtering
  283. */
  284. protected boolean doFilterContainer(boolean hasFilters) {
  285. if (!hasFilters) {
  286. boolean changed = getAllItemIds().size() != getVisibleItemIds()
  287. .size();
  288. setFilteredItemIds(null);
  289. return changed;
  290. }
  291. // Reset filtered list
  292. List<ITEMIDTYPE> originalFilteredItemIds = getFilteredItemIds();
  293. boolean wasUnfiltered = false;
  294. if (originalFilteredItemIds == null) {
  295. originalFilteredItemIds = Collections.emptyList();
  296. wasUnfiltered = true;
  297. }
  298. setFilteredItemIds(new ListSet<ITEMIDTYPE>());
  299. // Filter
  300. boolean equal = true;
  301. Iterator<ITEMIDTYPE> origIt = originalFilteredItemIds.iterator();
  302. for (final Iterator<ITEMIDTYPE> i = getAllItemIds().iterator(); i
  303. .hasNext();) {
  304. final ITEMIDTYPE id = i.next();
  305. if (passesFilters(id)) {
  306. // filtered list comes from the full list, can use ==
  307. equal = equal && origIt.hasNext() && origIt.next() == id;
  308. getFilteredItemIds().add(id);
  309. }
  310. }
  311. return (wasUnfiltered && !getAllItemIds().isEmpty()) || !equal
  312. || origIt.hasNext();
  313. }
  314. /**
  315. * Checks if the given itemId passes the filters set for the container. The
  316. * caller should make sure the itemId exists in the container. For
  317. * non-existing itemIds the behavior is undefined.
  318. *
  319. * @param itemId
  320. * An itemId that exists in the container.
  321. * @return true if the itemId passes all filters or no filters are set,
  322. * false otherwise.
  323. */
  324. protected boolean passesFilters(Object itemId) {
  325. ITEMCLASS item = getUnfilteredItem(itemId);
  326. if (getFilters().isEmpty()) {
  327. return true;
  328. }
  329. final Iterator<Filter> i = getFilters().iterator();
  330. while (i.hasNext()) {
  331. final Filter f = i.next();
  332. if (!f.passesFilter(itemId, item)) {
  333. return false;
  334. }
  335. }
  336. return true;
  337. }
  338. /**
  339. * Adds a container filter and re-filter the view.
  340. *
  341. * The filter must implement Filter and its sub-filters (if any) must also
  342. * be in-memory filterable.
  343. *
  344. * This can be used to implement
  345. * {@link Filterable#addContainerFilter(com.vaadin.data.Container.Filter)}
  346. * and optionally also
  347. * {@link SimpleFilterable#addContainerFilter(Object, String, boolean, boolean)}
  348. * (with {@link SimpleStringFilter}).
  349. *
  350. * Note that in some cases, incompatible filters cannot be detected when
  351. * added and an {@link UnsupportedFilterException} may occur when performing
  352. * filtering.
  353. *
  354. * @throws UnsupportedFilterException
  355. * if the filter is detected as not supported by the container
  356. */
  357. protected void addFilter(Filter filter) throws UnsupportedFilterException {
  358. getFilters().add(filter);
  359. filterAll();
  360. }
  361. /**
  362. * Remove a specific container filter and re-filter the view (if necessary).
  363. *
  364. * This can be used to implement
  365. * {@link Filterable#removeContainerFilter(com.vaadin.data.Container.Filter)}
  366. * .
  367. */
  368. protected void removeFilter(Filter filter) {
  369. for (Iterator<Filter> iterator = getFilters().iterator(); iterator
  370. .hasNext();) {
  371. Filter f = iterator.next();
  372. if (f.equals(filter)) {
  373. iterator.remove();
  374. filterAll();
  375. return;
  376. }
  377. }
  378. }
  379. /**
  380. * Remove all container filters for all properties and re-filter the view.
  381. *
  382. * This can be used to implement
  383. * {@link Filterable#removeAllContainerFilters()}.
  384. */
  385. protected void removeAllFilters() {
  386. if (getFilters().isEmpty()) {
  387. return;
  388. }
  389. getFilters().clear();
  390. filterAll();
  391. }
  392. /**
  393. * Checks if there is a filter that applies to a given property.
  394. *
  395. * @param propertyId
  396. * @return true if there is an active filter for the property
  397. */
  398. protected boolean isPropertyFiltered(Object propertyId) {
  399. if (getFilters().isEmpty() || propertyId == null) {
  400. return false;
  401. }
  402. final Iterator<Filter> i = getFilters().iterator();
  403. while (i.hasNext()) {
  404. final Filter f = i.next();
  405. if (f.appliesToProperty(propertyId)) {
  406. return true;
  407. }
  408. }
  409. return false;
  410. }
  411. /**
  412. * Remove all container filters for a given property identifier and
  413. * re-filter the view. This also removes filters applying to multiple
  414. * properties including the one identified by propertyId.
  415. *
  416. * This can be used to implement
  417. * {@link Filterable#removeContainerFilters(Object)}.
  418. *
  419. * @param propertyId
  420. * @return Collection<Filter> removed filters
  421. */
  422. protected Collection<Filter> removeFilters(Object propertyId) {
  423. if (getFilters().isEmpty() || propertyId == null) {
  424. return Collections.emptyList();
  425. }
  426. List<Filter> removedFilters = new LinkedList<Filter>();
  427. for (Iterator<Filter> iterator = getFilters().iterator(); iterator
  428. .hasNext();) {
  429. Filter f = iterator.next();
  430. if (f.appliesToProperty(propertyId)) {
  431. removedFilters.add(f);
  432. iterator.remove();
  433. }
  434. }
  435. if (!removedFilters.isEmpty()) {
  436. filterAll();
  437. return removedFilters;
  438. }
  439. return Collections.emptyList();
  440. }
  441. // sorting
  442. /**
  443. * Returns the ItemSorter used for comparing items in a sort. See
  444. * {@link #setItemSorter(ItemSorter)} for more information.
  445. *
  446. * @return The ItemSorter used for comparing two items in a sort.
  447. */
  448. protected ItemSorter getItemSorter() {
  449. return itemSorter;
  450. }
  451. /**
  452. * Sets the ItemSorter used for comparing items in a sort. The
  453. * {@link ItemSorter#compare(Object, Object)} method is called with item ids
  454. * to perform the sorting. A default ItemSorter is used if this is not
  455. * explicitly set.
  456. *
  457. * @param itemSorter
  458. * The ItemSorter used for comparing two items in a sort (not
  459. * null).
  460. */
  461. protected void setItemSorter(ItemSorter itemSorter) {
  462. this.itemSorter = itemSorter;
  463. }
  464. /**
  465. * Sort base implementation to be used to implement {@link Sortable}.
  466. *
  467. * Subclasses should call this from a public
  468. * {@link #sort(Object[], boolean[])} method when implementing Sortable.
  469. *
  470. * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[],
  471. * boolean[])
  472. */
  473. protected void sortContainer(Object[] propertyId, boolean[] ascending) {
  474. if (!(this instanceof Sortable)) {
  475. throw new UnsupportedOperationException(
  476. "Cannot sort a Container that does not implement Sortable");
  477. }
  478. // Set up the item sorter for the sort operation
  479. getItemSorter().setSortProperties((Sortable) this, propertyId,
  480. ascending);
  481. // Perform the actual sort
  482. doSort();
  483. // Post sort updates
  484. if (isFiltered()) {
  485. filterAll();
  486. } else {
  487. fireItemSetChange();
  488. }
  489. }
  490. /**
  491. * Perform the sorting of the data structures in the container. This is
  492. * invoked when the <code>itemSorter</code> has been prepared for the sort
  493. * operation. Typically this method calls
  494. * <code>Collections.sort(aCollection, getItemSorter())</code> on all arrays
  495. * (containing item ids) that need to be sorted.
  496. *
  497. */
  498. protected void doSort() {
  499. Collections.sort(getAllItemIds(), getItemSorter());
  500. }
  501. /**
  502. * Returns the sortable property identifiers for the container. Can be used
  503. * to implement {@link Sortable#getSortableContainerPropertyIds()}.
  504. */
  505. protected Collection<?> getSortablePropertyIds() {
  506. LinkedList<Object> sortables = new LinkedList<Object>();
  507. for (Object propertyId : getContainerPropertyIds()) {
  508. Class<?> propertyType = getType(propertyId);
  509. if (Comparable.class.isAssignableFrom(propertyType)
  510. || propertyType.isPrimitive()) {
  511. sortables.add(propertyId);
  512. }
  513. }
  514. return sortables;
  515. }
  516. // removing items
  517. /**
  518. * Removes all items from the internal data structures of this class. This
  519. * can be used to implement {@link #removeAllItems()} in subclasses.
  520. *
  521. * No notification is sent, the caller has to fire a suitable item set
  522. * change notification.
  523. */
  524. protected void internalRemoveAllItems() {
  525. // Removes all Items
  526. getAllItemIds().clear();
  527. if (isFiltered()) {
  528. getFilteredItemIds().clear();
  529. }
  530. }
  531. /**
  532. * Removes a single item from the internal data structures of this class.
  533. * This can be used to implement {@link #removeItem(Object)} in subclasses.
  534. *
  535. * No notification is sent, the caller has to fire a suitable item set
  536. * change notification.
  537. *
  538. * @param itemId
  539. * the identifier of the item to remove
  540. * @return true if an item was successfully removed, false if failed to
  541. * remove or no such item
  542. */
  543. protected boolean internalRemoveItem(Object itemId) {
  544. if (itemId == null) {
  545. return false;
  546. }
  547. boolean result = getAllItemIds().remove(itemId);
  548. if (result && isFiltered()) {
  549. getFilteredItemIds().remove(itemId);
  550. }
  551. return result;
  552. }
  553. // adding items
  554. /**
  555. * Adds the bean to all internal data structures at the given position.
  556. * Fails if an item with itemId is already in the container. Returns a the
  557. * item if it was added successfully, null otherwise.
  558. *
  559. * <p>
  560. * Caller should initiate filtering after calling this method.
  561. * </p>
  562. *
  563. * For internal use only - subclasses should use
  564. * {@link #internalAddItemAtEnd(Object, Item, boolean)},
  565. * {@link #internalAddItemAt(int, Object, Item, boolean)} and
  566. * {@link #internalAddItemAfter(Object, Object, Item, boolean)} instead.
  567. *
  568. * @param position
  569. * The position at which the item should be inserted in the
  570. * unfiltered collection of items
  571. * @param itemId
  572. * The item identifier for the item to insert
  573. * @param item
  574. * The item to insert
  575. *
  576. * @return ITEMCLASS if the item was added successfully, null otherwise
  577. */
  578. private ITEMCLASS internalAddAt(int position, ITEMIDTYPE itemId,
  579. ITEMCLASS item) {
  580. if (position < 0 || position > getAllItemIds().size() || itemId == null
  581. || item == null) {
  582. return null;
  583. }
  584. // Make sure that the item has not been added previously
  585. if (getAllItemIds().contains(itemId)) {
  586. return null;
  587. }
  588. // "filteredList" will be updated in filterAll() which should be invoked
  589. // by the caller after calling this method.
  590. getAllItemIds().add(position, itemId);
  591. registerNewItem(position, itemId, item);
  592. return item;
  593. }
  594. /**
  595. * Add an item at the end of the container, and perform filtering if
  596. * necessary. An event is fired if the filtered view changes.
  597. *
  598. * @param newItemId
  599. * @param item
  600. * new item to add
  601. * @param filter
  602. * true to perform filtering and send event after adding the
  603. * item, false to skip these operations for batch inserts - if
  604. * false, caller needs to make sure these operations are
  605. * performed at the end of the batch
  606. * @return item added or null if no item was added
  607. */
  608. protected ITEMCLASS internalAddItemAtEnd(ITEMIDTYPE newItemId,
  609. ITEMCLASS item, boolean filter) {
  610. ITEMCLASS newItem = internalAddAt(getAllItemIds().size(), newItemId,
  611. item);
  612. if (newItem != null && filter) {
  613. // TODO filter only this item, use fireItemAdded()
  614. filterAll();
  615. if (!isFiltered()) {
  616. // TODO hack: does not detect change in filterAll() in this case
  617. fireItemAdded(indexOfId(newItemId), newItemId, item);
  618. }
  619. }
  620. return newItem;
  621. }
  622. /**
  623. * Add an item after a given (visible) item, and perform filtering. An event
  624. * is fired if the filtered view changes.
  625. *
  626. * The new item is added at the beginning if previousItemId is null.
  627. *
  628. * @param previousItemId
  629. * item id of a visible item after which to add the new item, or
  630. * null to add at the beginning
  631. * @param newItemId
  632. * @param item
  633. * new item to add
  634. * @param filter
  635. * true to perform filtering and send event after adding the
  636. * item, false to skip these operations for batch inserts - if
  637. * false, caller needs to make sure these operations are
  638. * performed at the end of the batch
  639. * @return item added or null if no item was added
  640. */
  641. protected ITEMCLASS internalAddItemAfter(ITEMIDTYPE previousItemId,
  642. ITEMIDTYPE newItemId, ITEMCLASS item, boolean filter) {
  643. // only add if the previous item is visible
  644. ITEMCLASS newItem = null;
  645. if (previousItemId == null) {
  646. newItem = internalAddAt(0, newItemId, item);
  647. } else if (containsId(previousItemId)) {
  648. newItem = internalAddAt(
  649. getAllItemIds().indexOf(previousItemId) + 1, newItemId,
  650. item);
  651. }
  652. if (newItem != null && filter) {
  653. // TODO filter only this item, use fireItemAdded()
  654. filterAll();
  655. if (!isFiltered()) {
  656. // TODO hack: does not detect change in filterAll() in this case
  657. fireItemAdded(indexOfId(newItemId), newItemId, item);
  658. }
  659. }
  660. return newItem;
  661. }
  662. /**
  663. * Add an item at a given (visible after filtering) item index, and perform
  664. * filtering. An event is fired if the filtered view changes.
  665. *
  666. * @param index
  667. * position where to add the item (visible/view index)
  668. * @param newItemId
  669. * @param item
  670. * new item to add
  671. * @param filter
  672. * true to perform filtering and send event after adding the
  673. * item, false to skip these operations for batch inserts - if
  674. * false, caller needs to make sure these operations are
  675. * performed at the end of the batch
  676. * @return item added or null if no item was added
  677. */
  678. protected ITEMCLASS internalAddItemAt(int index, ITEMIDTYPE newItemId,
  679. ITEMCLASS item, boolean filter) {
  680. if (index < 0 || index > size()) {
  681. return null;
  682. } else if (index == 0) {
  683. // add before any item, visible or not
  684. return internalAddItemAfter(null, newItemId, item, filter);
  685. } else {
  686. // if index==size(), adds immediately after last visible item
  687. return internalAddItemAfter(getIdByIndex(index - 1), newItemId,
  688. item, filter);
  689. }
  690. }
  691. /**
  692. * Registers a new item as having been added to the container. This can
  693. * involve storing the item or any relevant information about it in internal
  694. * container-specific collections if necessary, as well as registering
  695. * listeners etc.
  696. *
  697. * The full identifier list in {@link AbstractInMemoryContainer} has already
  698. * been updated to reflect the new item when this method is called.
  699. *
  700. * @param position
  701. * @param itemId
  702. * @param item
  703. */
  704. protected void registerNewItem(int position, ITEMIDTYPE itemId,
  705. ITEMCLASS item) {
  706. }
  707. // item set change notifications
  708. /**
  709. * Notify item set change listeners that an item has been added to the
  710. * container.
  711. *
  712. * Unless subclasses specify otherwise, the default notification indicates a
  713. * full refresh.
  714. *
  715. * @param postion
  716. * position of the added item in the view (if visible)
  717. * @param itemId
  718. * id of the added item
  719. * @param item
  720. * the added item
  721. */
  722. protected void fireItemAdded(int position, ITEMIDTYPE itemId, ITEMCLASS item) {
  723. fireItemSetChange();
  724. }
  725. /**
  726. * Notify item set change listeners that an item has been removed from the
  727. * container.
  728. *
  729. * Unless subclasses specify otherwise, the default notification indicates a
  730. * full refresh.
  731. *
  732. * @param postion
  733. * position of the removed item in the view prior to removal (if
  734. * was visible)
  735. * @param itemId
  736. * id of the removed item, of type {@link Object} to satisfy
  737. * {@link Container#removeItem(Object)} API
  738. */
  739. protected void fireItemRemoved(int position, Object itemId) {
  740. fireItemSetChange();
  741. }
  742. // visible and filtered item identifier lists
  743. /**
  744. * Returns the internal list of visible item identifiers after filtering.
  745. *
  746. * For internal use only.
  747. */
  748. protected List<ITEMIDTYPE> getVisibleItemIds() {
  749. if (isFiltered()) {
  750. return getFilteredItemIds();
  751. } else {
  752. return getAllItemIds();
  753. }
  754. }
  755. /**
  756. * Returns true is the container has active filters.
  757. *
  758. * @return true if the container is currently filtered
  759. */
  760. protected boolean isFiltered() {
  761. return filteredItemIds != null;
  762. }
  763. /**
  764. * Internal helper method to set the internal list of filtered item
  765. * identifiers. Should not be used outside this class except for
  766. * implementing clone(), may disappear from future versions.
  767. *
  768. * @param filteredItemIds
  769. */
  770. @Deprecated
  771. protected void setFilteredItemIds(List<ITEMIDTYPE> filteredItemIds) {
  772. this.filteredItemIds = filteredItemIds;
  773. }
  774. /**
  775. * Internal helper method to get the internal list of filtered item
  776. * identifiers. Should not be used outside this class except for
  777. * implementing clone(), may disappear from future versions - use
  778. * {@link #getVisibleItemIds()} in other contexts.
  779. *
  780. * @return List<ITEMIDTYPE>
  781. */
  782. protected List<ITEMIDTYPE> getFilteredItemIds() {
  783. return filteredItemIds;
  784. }
  785. /**
  786. * Internal helper method to set the internal list of all item identifiers.
  787. * Should not be used outside this class except for implementing clone(),
  788. * may disappear from future versions.
  789. *
  790. * @param allItemIds
  791. */
  792. @Deprecated
  793. protected void setAllItemIds(List<ITEMIDTYPE> allItemIds) {
  794. this.allItemIds = allItemIds;
  795. }
  796. /**
  797. * Internal helper method to get the internal list of all item identifiers.
  798. * Avoid using this method outside this class, may disappear in future
  799. * versions.
  800. *
  801. * @return List<ITEMIDTYPE>
  802. */
  803. protected List<ITEMIDTYPE> getAllItemIds() {
  804. return allItemIds;
  805. }
  806. /**
  807. * Set the internal collection of filters without performing filtering.
  808. *
  809. * This method is mostly for internal use, use
  810. * {@link #addFilter(com.vaadin.data.Container.Filter)} and
  811. * <code>remove*Filter*</code> (which also re-filter the container) instead
  812. * when possible.
  813. *
  814. * @param filters
  815. */
  816. protected void setFilters(Set<Filter> filters) {
  817. this.filters = filters;
  818. }
  819. /**
  820. * Returns the internal collection of filters. The returned collection
  821. * should not be modified by callers outside this class.
  822. *
  823. * @return Set<Filter>
  824. */
  825. protected Set<Filter> getFilters() {
  826. return filters;
  827. }
  828. }