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

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