Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

AbstractInMemoryContainer.java 31KB

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