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.

AbstractBeanContainer.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  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.HashMap;
  20. import java.util.Iterator;
  21. import java.util.LinkedHashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import com.vaadin.data.provider.DataProvider;
  25. import com.vaadin.v7.data.Container;
  26. import com.vaadin.v7.data.Container.Filterable;
  27. import com.vaadin.v7.data.Container.PropertySetChangeNotifier;
  28. import com.vaadin.v7.data.Container.SimpleFilterable;
  29. import com.vaadin.v7.data.Container.Sortable;
  30. import com.vaadin.v7.data.Item;
  31. import com.vaadin.v7.data.Property;
  32. import com.vaadin.v7.data.Property.ValueChangeEvent;
  33. import com.vaadin.v7.data.Property.ValueChangeListener;
  34. import com.vaadin.v7.data.Property.ValueChangeNotifier;
  35. import com.vaadin.v7.data.util.MethodProperty.MethodException;
  36. import com.vaadin.v7.data.util.filter.SimpleStringFilter;
  37. import com.vaadin.v7.data.util.filter.UnsupportedFilterException;
  38. /**
  39. * An abstract base class for in-memory containers for JavaBeans.
  40. *
  41. * <p>
  42. * The properties of the container are determined automatically by introspecting
  43. * the used JavaBean class and explicitly adding or removing properties is not
  44. * supported. Only beans of the same type can be added to the container.
  45. * </p>
  46. *
  47. * <p>
  48. * Subclasses should implement any public methods adding items to the container,
  49. * typically calling the protected methods {@link #addItem(Object, Object)},
  50. * {@link #addItemAfter(Object, Object, Object)} and
  51. * {@link #addItemAt(int, Object, Object)}.
  52. * </p>
  53. *
  54. * @param <IDTYPE>
  55. * The type of the item identifier
  56. * @param <BEANTYPE>
  57. * The type of the Bean
  58. *
  59. * @since 6.5
  60. *
  61. *
  62. * @deprecated As of 8.0, replaced by {@link DataProvider}
  63. */
  64. @Deprecated
  65. public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE>
  66. extends AbstractInMemoryContainer<IDTYPE, String, BeanItem<BEANTYPE>>
  67. implements Filterable, SimpleFilterable, Sortable, ValueChangeListener,
  68. PropertySetChangeNotifier {
  69. /**
  70. * Resolver that maps beans to their (item) identifiers, removing the need
  71. * to explicitly specify item identifiers when there is no need to customize
  72. * this.
  73. *
  74. * Note that beans can also be added with an explicit id even if a resolver
  75. * has been set.
  76. *
  77. * @param <IDTYPE>
  78. * @param <BEANTYPE>
  79. *
  80. * @since 6.5
  81. */
  82. @Deprecated
  83. public static interface BeanIdResolver<IDTYPE, BEANTYPE>
  84. extends Serializable {
  85. /**
  86. * Return the item identifier for a bean.
  87. *
  88. * @param bean
  89. * @return
  90. */
  91. public IDTYPE getIdForBean(BEANTYPE bean);
  92. }
  93. /**
  94. * A item identifier resolver that returns the value of a bean property.
  95. *
  96. * The bean must have a getter for the property, and the getter must return
  97. * an object of type IDTYPE.
  98. */
  99. @Deprecated
  100. protected class PropertyBasedBeanIdResolver
  101. implements BeanIdResolver<IDTYPE, BEANTYPE> {
  102. private final Object propertyId;
  103. public PropertyBasedBeanIdResolver(Object propertyId) {
  104. if (propertyId == null) {
  105. throw new IllegalArgumentException(
  106. "Property identifier must not be null");
  107. }
  108. this.propertyId = propertyId;
  109. }
  110. @Override
  111. @SuppressWarnings("unchecked")
  112. public IDTYPE getIdForBean(BEANTYPE bean)
  113. throws IllegalArgumentException {
  114. VaadinPropertyDescriptor<BEANTYPE> pd = model.get(propertyId);
  115. if (null == pd) {
  116. throw new IllegalStateException(
  117. "Property " + propertyId + " not found");
  118. }
  119. try {
  120. Property<IDTYPE> property = (Property<IDTYPE>) pd
  121. .createProperty(bean);
  122. return property.getValue();
  123. } catch (MethodException e) {
  124. throw new IllegalArgumentException(e);
  125. }
  126. }
  127. }
  128. /**
  129. * The resolver that finds the item ID for a bean, or null not to use
  130. * automatic resolving.
  131. *
  132. * Methods that add a bean without specifying an ID must not be called if no
  133. * resolver has been set.
  134. */
  135. private BeanIdResolver<IDTYPE, BEANTYPE> beanIdResolver = null;
  136. /**
  137. * Maps all item ids in the container (including filtered) to their
  138. * corresponding BeanItem.
  139. */
  140. private final Map<IDTYPE, BeanItem<BEANTYPE>> itemIdToItem = new HashMap<IDTYPE, BeanItem<BEANTYPE>>();
  141. /**
  142. * The type of the beans in the container.
  143. */
  144. private final Class<? super BEANTYPE> type;
  145. /**
  146. * A description of the properties found in beans of type {@link #type}.
  147. * Determines the property ids that are present in the container.
  148. */
  149. private final LinkedHashMap<String, VaadinPropertyDescriptor<BEANTYPE>> model;
  150. /**
  151. * Constructs a {@code AbstractBeanContainer} for beans of the given type.
  152. *
  153. * @param type
  154. * the type of the beans that will be added to the container.
  155. * @throws IllegalArgumentException
  156. * If {@code type} is null
  157. */
  158. protected AbstractBeanContainer(Class<? super BEANTYPE> type) {
  159. if (type == null) {
  160. throw new IllegalArgumentException(
  161. "The bean type passed to AbstractBeanContainer must not be null");
  162. }
  163. this.type = type;
  164. model = BeanItem.getPropertyDescriptors((Class<BEANTYPE>) type);
  165. }
  166. @Override
  167. public Class<?> getType(Object propertyId) {
  168. VaadinPropertyDescriptor<BEANTYPE> descriptor = model.get(propertyId);
  169. if (descriptor == null) {
  170. return null;
  171. }
  172. return descriptor.getPropertyType();
  173. }
  174. /**
  175. * Create a BeanItem for a bean using pre-parsed bean metadata (based on
  176. * {@link #getBeanType()}).
  177. *
  178. * @param bean
  179. * @return created {@link BeanItem} or null if bean is null
  180. */
  181. protected BeanItem<BEANTYPE> createBeanItem(BEANTYPE bean) {
  182. return bean == null ? null : new BeanItem<BEANTYPE>(bean, model);
  183. }
  184. /**
  185. * Returns the type of beans this Container can contain.
  186. *
  187. * This comes from the bean type constructor parameter, and bean metadata
  188. * (including container properties) is based on this.
  189. *
  190. * @return
  191. */
  192. public Class<? super BEANTYPE> getBeanType() {
  193. return type;
  194. }
  195. @Override
  196. public Collection<String> getContainerPropertyIds() {
  197. return model.keySet();
  198. }
  199. @Override
  200. public boolean removeAllItems() {
  201. int origSize = size();
  202. IDTYPE firstItem = getFirstVisibleItem();
  203. internalRemoveAllItems();
  204. // detach listeners from all Items
  205. for (Item item : itemIdToItem.values()) {
  206. removeAllValueChangeListeners(item);
  207. }
  208. itemIdToItem.clear();
  209. // fire event only if the visible view changed, regardless of whether
  210. // filtered out items were removed or not
  211. if (origSize != 0) {
  212. fireItemsRemoved(0, firstItem, origSize);
  213. }
  214. return true;
  215. }
  216. @Override
  217. public BeanItem<BEANTYPE> getItem(Object itemId) {
  218. // TODO return only if visible?
  219. return getUnfilteredItem(itemId);
  220. }
  221. @Override
  222. protected BeanItem<BEANTYPE> getUnfilteredItem(Object itemId) {
  223. return itemIdToItem.get(itemId);
  224. }
  225. @Override
  226. @SuppressWarnings("unchecked")
  227. public List<IDTYPE> getItemIds() {
  228. return (List<IDTYPE>) super.getItemIds();
  229. }
  230. @Override
  231. public Property getContainerProperty(Object itemId, Object propertyId) {
  232. Item item = getItem(itemId);
  233. if (item == null) {
  234. return null;
  235. }
  236. return item.getItemProperty(propertyId);
  237. }
  238. @Override
  239. public boolean removeItem(Object itemId) {
  240. // TODO should also remove items that are filtered out
  241. int origSize = size();
  242. Item item = getItem(itemId);
  243. int position = indexOfId(itemId);
  244. if (internalRemoveItem(itemId)) {
  245. // detach listeners from Item
  246. removeAllValueChangeListeners(item);
  247. // remove item
  248. itemIdToItem.remove(itemId);
  249. // fire event only if the visible view changed, regardless of
  250. // whether filtered out items were removed or not
  251. if (size() != origSize) {
  252. fireItemRemoved(position, itemId);
  253. }
  254. return true;
  255. } else {
  256. return false;
  257. }
  258. }
  259. /**
  260. * Re-filter the container when one of the monitored properties changes.
  261. */
  262. @Override
  263. public void valueChange(ValueChangeEvent event) {
  264. // if a property that is used in a filter is changed, refresh filtering
  265. filterAll();
  266. }
  267. @Override
  268. public void addContainerFilter(Object propertyId, String filterString,
  269. boolean ignoreCase, boolean onlyMatchPrefix) {
  270. try {
  271. addFilter(new SimpleStringFilter(propertyId, filterString,
  272. ignoreCase, onlyMatchPrefix));
  273. } catch (UnsupportedFilterException e) {
  274. // the filter instance created here is always valid for in-memory
  275. // containers
  276. }
  277. }
  278. @Override
  279. public void removeAllContainerFilters() {
  280. if (!getFilters().isEmpty()) {
  281. for (Item item : itemIdToItem.values()) {
  282. removeAllValueChangeListeners(item);
  283. }
  284. removeAllFilters();
  285. }
  286. }
  287. @Override
  288. public void removeContainerFilters(Object propertyId) {
  289. Collection<Filter> removedFilters = super.removeFilters(propertyId);
  290. if (!removedFilters.isEmpty()) {
  291. // stop listening to change events for the property
  292. for (Item item : itemIdToItem.values()) {
  293. removeValueChangeListener(item, propertyId);
  294. }
  295. }
  296. }
  297. @Override
  298. public void addContainerFilter(Filter filter)
  299. throws UnsupportedFilterException {
  300. addFilter(filter);
  301. }
  302. @Override
  303. public void removeContainerFilter(Filter filter) {
  304. removeFilter(filter);
  305. }
  306. @Override
  307. public boolean hasContainerFilters() {
  308. return super.hasContainerFilters();
  309. }
  310. @Override
  311. public Collection<Filter> getContainerFilters() {
  312. return super.getContainerFilters();
  313. }
  314. /**
  315. * Make this container listen to the given property provided it notifies
  316. * when its value changes.
  317. *
  318. * @param item
  319. * The {@link Item} that contains the property
  320. * @param propertyId
  321. * The id of the property
  322. */
  323. private void addValueChangeListener(Item item, Object propertyId) {
  324. Property<?> property = item.getItemProperty(propertyId);
  325. if (property instanceof ValueChangeNotifier) {
  326. // avoid multiple notifications for the same property if
  327. // multiple filters are in use
  328. ValueChangeNotifier notifier = (ValueChangeNotifier) property;
  329. notifier.removeListener(this);
  330. notifier.addListener(this);
  331. }
  332. }
  333. /**
  334. * Remove this container as a listener for the given property.
  335. *
  336. * @param item
  337. * The {@link Item} that contains the property
  338. * @param propertyId
  339. * The id of the property
  340. */
  341. private void removeValueChangeListener(Item item, Object propertyId) {
  342. Property<?> property = item.getItemProperty(propertyId);
  343. if (property instanceof ValueChangeNotifier) {
  344. ((ValueChangeNotifier) property).removeListener(this);
  345. }
  346. }
  347. /**
  348. * Remove this contains as a listener for all the properties in the given
  349. * {@link Item}.
  350. *
  351. * @param item
  352. * The {@link Item} that contains the properties
  353. */
  354. private void removeAllValueChangeListeners(Item item) {
  355. for (Object propertyId : item.getItemPropertyIds()) {
  356. removeValueChangeListener(item, propertyId);
  357. }
  358. }
  359. @Override
  360. public Collection<?> getSortableContainerPropertyIds() {
  361. return getSortablePropertyIds();
  362. }
  363. @Override
  364. public void sort(Object[] propertyId, boolean[] ascending) {
  365. sortContainer(propertyId, ascending);
  366. }
  367. @Override
  368. public ItemSorter getItemSorter() {
  369. return super.getItemSorter();
  370. }
  371. @Override
  372. public void setItemSorter(ItemSorter itemSorter) {
  373. super.setItemSorter(itemSorter);
  374. }
  375. @Override
  376. protected void registerNewItem(int position, IDTYPE itemId,
  377. BeanItem<BEANTYPE> item) {
  378. itemIdToItem.put(itemId, item);
  379. // add listeners to be able to update filtering on property
  380. // changes
  381. for (Filter filter : getFilters()) {
  382. for (String propertyId : getContainerPropertyIds()) {
  383. if (filter.appliesToProperty(propertyId)) {
  384. // addValueChangeListener avoids adding duplicates
  385. addValueChangeListener(item, propertyId);
  386. }
  387. }
  388. }
  389. }
  390. /**
  391. * Check that a bean can be added to the container (is of the correct type
  392. * for the container).
  393. *
  394. * @param bean
  395. * @return
  396. */
  397. private boolean validateBean(BEANTYPE bean) {
  398. return bean != null && getBeanType().isAssignableFrom(bean.getClass());
  399. }
  400. /**
  401. * Adds the bean to the Container.
  402. *
  403. * Note: the behavior of this method changed in Vaadin 6.6 - now items are
  404. * added at the very end of the unfiltered container and not after the last
  405. * visible item if filtering is used.
  406. *
  407. * @see Container#addItem(Object)
  408. */
  409. protected BeanItem<BEANTYPE> addItem(IDTYPE itemId, BEANTYPE bean) {
  410. if (!validateBean(bean)) {
  411. return null;
  412. }
  413. return internalAddItemAtEnd(itemId, createBeanItem(bean), true);
  414. }
  415. /**
  416. * Adds the bean after the given bean.
  417. *
  418. * @see Container.Ordered#addItemAfter(Object, Object)
  419. */
  420. protected BeanItem<BEANTYPE> addItemAfter(IDTYPE previousItemId,
  421. IDTYPE newItemId, BEANTYPE bean) {
  422. if (!validateBean(bean)) {
  423. return null;
  424. }
  425. return internalAddItemAfter(previousItemId, newItemId,
  426. createBeanItem(bean), true);
  427. }
  428. /**
  429. * Adds a new bean at the given index.
  430. *
  431. * The bean is used both as the item contents and as the item identifier.
  432. *
  433. * @param index
  434. * Index at which the bean should be added.
  435. * @param newItemId
  436. * The item id for the bean to add to the container.
  437. * @param bean
  438. * The bean to add to the container.
  439. *
  440. * @return Returns the new BeanItem or null if the operation fails.
  441. */
  442. protected BeanItem<BEANTYPE> addItemAt(int index, IDTYPE newItemId,
  443. BEANTYPE bean) {
  444. if (!validateBean(bean)) {
  445. return null;
  446. }
  447. return internalAddItemAt(index, newItemId, createBeanItem(bean), true);
  448. }
  449. /**
  450. * Adds a bean to the container using the bean item id resolver to find its
  451. * identifier.
  452. *
  453. * A bean id resolver must be set before calling this method.
  454. *
  455. * @see #addItem(Object, Object)
  456. *
  457. * @param bean
  458. * the bean to add
  459. * @return BeanItem<BEANTYPE> item added or null
  460. * @throws IllegalStateException
  461. * if no bean identifier resolver has been set
  462. * @throws IllegalArgumentException
  463. * if an identifier cannot be resolved for the bean
  464. */
  465. protected BeanItem<BEANTYPE> addBean(BEANTYPE bean)
  466. throws IllegalStateException, IllegalArgumentException {
  467. if (bean == null) {
  468. return null;
  469. }
  470. IDTYPE itemId = resolveBeanId(bean);
  471. if (itemId == null) {
  472. throw new IllegalArgumentException(
  473. "Resolved identifier for a bean must not be null");
  474. }
  475. return addItem(itemId, bean);
  476. }
  477. /**
  478. * Adds a bean to the container after a specified item identifier, using the
  479. * bean item id resolver to find its identifier.
  480. *
  481. * A bean id resolver must be set before calling this method.
  482. *
  483. * @see #addItemAfter(Object, Object, Object)
  484. *
  485. * @param previousItemId
  486. * the identifier of the bean after which this bean should be
  487. * added, null to add to the beginning
  488. * @param bean
  489. * the bean to add
  490. * @return BeanItem<BEANTYPE> item added or null
  491. * @throws IllegalStateException
  492. * if no bean identifier resolver has been set
  493. * @throws IllegalArgumentException
  494. * if an identifier cannot be resolved for the bean
  495. */
  496. protected BeanItem<BEANTYPE> addBeanAfter(IDTYPE previousItemId,
  497. BEANTYPE bean)
  498. throws IllegalStateException, IllegalArgumentException {
  499. if (bean == null) {
  500. return null;
  501. }
  502. IDTYPE itemId = resolveBeanId(bean);
  503. if (itemId == null) {
  504. throw new IllegalArgumentException(
  505. "Resolved identifier for a bean must not be null");
  506. }
  507. return addItemAfter(previousItemId, itemId, bean);
  508. }
  509. /**
  510. * Adds a bean at a specified (filtered view) position in the container
  511. * using the bean item id resolver to find its identifier.
  512. *
  513. * A bean id resolver must be set before calling this method.
  514. *
  515. * @see #addItemAfter(Object, Object, Object)
  516. *
  517. * @param index
  518. * the index (in the filtered view) at which to add the item
  519. * @param bean
  520. * the bean to add
  521. * @return BeanItem<BEANTYPE> item added or null
  522. * @throws IllegalStateException
  523. * if no bean identifier resolver has been set
  524. * @throws IllegalArgumentException
  525. * if an identifier cannot be resolved for the bean
  526. */
  527. protected BeanItem<BEANTYPE> addBeanAt(int index, BEANTYPE bean)
  528. throws IllegalStateException, IllegalArgumentException {
  529. if (bean == null) {
  530. return null;
  531. }
  532. IDTYPE itemId = resolveBeanId(bean);
  533. if (itemId == null) {
  534. throw new IllegalArgumentException(
  535. "Resolved identifier for a bean must not be null");
  536. }
  537. return addItemAt(index, itemId, bean);
  538. }
  539. /**
  540. * Adds all the beans from a {@link Collection} in one operation using the
  541. * bean item identifier resolver. More efficient than adding them one by
  542. * one.
  543. *
  544. * A bean id resolver must be set before calling this method.
  545. *
  546. * Note: the behavior of this method changed in Vaadin 6.6 - now items are
  547. * added at the very end of the unfiltered container and not after the last
  548. * visible item if filtering is used.
  549. *
  550. * @param collection
  551. * The collection of beans to add. Must not be null.
  552. * @throws IllegalStateException
  553. * if no bean identifier resolver has been set
  554. * @throws IllegalArgumentException
  555. * if the resolver returns a null itemId for one of the beans in
  556. * the collection
  557. */
  558. protected void addAll(Collection<? extends BEANTYPE> collection)
  559. throws IllegalStateException, IllegalArgumentException {
  560. boolean modified = false;
  561. int origSize = size();
  562. for (BEANTYPE bean : collection) {
  563. // TODO skipping invalid beans - should not allow them in javadoc?
  564. if (bean == null
  565. || !getBeanType().isAssignableFrom(bean.getClass())) {
  566. continue;
  567. }
  568. IDTYPE itemId = resolveBeanId(bean);
  569. if (itemId == null) {
  570. throw new IllegalArgumentException(
  571. "Resolved identifier for a bean must not be null");
  572. }
  573. if (internalAddItemAtEnd(itemId, createBeanItem(bean),
  574. false) != null) {
  575. modified = true;
  576. }
  577. }
  578. if (modified) {
  579. // Filter the contents when all items have been added
  580. if (isFiltered()) {
  581. doFilterContainer(!getFilters().isEmpty());
  582. }
  583. if (visibleNewItemsWasAdded(origSize)) {
  584. // fire event about added items
  585. int firstPosition = origSize;
  586. IDTYPE firstItemId = getVisibleItemIds().get(firstPosition);
  587. int affectedItems = size() - origSize;
  588. fireItemsAdded(firstPosition, firstItemId, affectedItems);
  589. }
  590. }
  591. }
  592. private boolean visibleNewItemsWasAdded(int origSize) {
  593. return size() > origSize;
  594. }
  595. /**
  596. * Use the bean resolver to get the identifier for a bean.
  597. *
  598. * @param bean
  599. * @return resolved bean identifier, null if could not be resolved
  600. * @throws IllegalStateException
  601. * if no bean resolver is set
  602. */
  603. protected IDTYPE resolveBeanId(BEANTYPE bean) {
  604. if (beanIdResolver == null) {
  605. throw new IllegalStateException(
  606. "Bean item identifier resolver is required.");
  607. }
  608. return beanIdResolver.getIdForBean(bean);
  609. }
  610. /**
  611. * Sets the resolver that finds the item id for a bean, or null not to use
  612. * automatic resolving.
  613. *
  614. * Methods that add a bean without specifying an id must not be called if no
  615. * resolver has been set.
  616. *
  617. * Note that methods taking an explicit id can be used whether a resolver
  618. * has been defined or not.
  619. *
  620. * @param beanIdResolver
  621. * to use or null to disable automatic id resolution
  622. */
  623. protected void setBeanIdResolver(
  624. BeanIdResolver<IDTYPE, BEANTYPE> beanIdResolver) {
  625. this.beanIdResolver = beanIdResolver;
  626. }
  627. /**
  628. * Returns the resolver that finds the item ID for a bean.
  629. *
  630. * @return resolver used or null if automatic item id resolving is disabled
  631. */
  632. public BeanIdResolver<IDTYPE, BEANTYPE> getBeanIdResolver() {
  633. return beanIdResolver;
  634. }
  635. /**
  636. * Create an item identifier resolver using a named bean property.
  637. *
  638. * @param propertyId
  639. * property identifier, which must map to a getter in BEANTYPE
  640. * @return created resolver
  641. */
  642. protected BeanIdResolver<IDTYPE, BEANTYPE> createBeanPropertyResolver(
  643. Object propertyId) {
  644. return new PropertyBasedBeanIdResolver(propertyId);
  645. }
  646. /**
  647. * @deprecated As of 7.0, replaced by {@link #addPropertySetChangeListener}
  648. **/
  649. @Deprecated
  650. @Override
  651. public void addListener(Container.PropertySetChangeListener listener) {
  652. addPropertySetChangeListener(listener);
  653. }
  654. @Override
  655. public void addPropertySetChangeListener(
  656. Container.PropertySetChangeListener listener) {
  657. super.addPropertySetChangeListener(listener);
  658. }
  659. /**
  660. * @deprecated As of 7.0, replaced by
  661. * {@link #removePropertySetChangeListener(Container.PropertySetChangeListener)}
  662. **/
  663. @Deprecated
  664. @Override
  665. public void removeListener(Container.PropertySetChangeListener listener) {
  666. removePropertySetChangeListener(listener);
  667. }
  668. @Override
  669. public void removePropertySetChangeListener(
  670. Container.PropertySetChangeListener listener) {
  671. super.removePropertySetChangeListener(listener);
  672. }
  673. @Override
  674. public boolean addContainerProperty(Object propertyId, Class<?> type,
  675. Object defaultValue) throws UnsupportedOperationException {
  676. throw new UnsupportedOperationException(
  677. "Use addNestedContainerProperty(String) to add container properties to a "
  678. + getClass().getSimpleName());
  679. }
  680. /**
  681. * Adds a property for the container and all its items.
  682. *
  683. * Primarily for internal use, may change in future versions.
  684. *
  685. * @param propertyId
  686. * @param propertyDescriptor
  687. * @return true if the property was added
  688. */
  689. protected final boolean addContainerProperty(String propertyId,
  690. VaadinPropertyDescriptor<BEANTYPE> propertyDescriptor) {
  691. if (null == propertyId || null == propertyDescriptor) {
  692. return false;
  693. }
  694. // Fails if the Property is already present
  695. if (model.containsKey(propertyId)) {
  696. return false;
  697. }
  698. model.put(propertyId, propertyDescriptor);
  699. for (BeanItem<BEANTYPE> item : itemIdToItem.values()) {
  700. item.addItemProperty(propertyId,
  701. propertyDescriptor.createProperty(item.getBean()));
  702. }
  703. // Sends a change event
  704. fireContainerPropertySetChange();
  705. return true;
  706. }
  707. /**
  708. * Adds a nested container property for the container, e.g.
  709. * "manager.address.street".
  710. *
  711. * All intermediate getters must exist and should return non-null values
  712. * when the property value is accessed. If an intermediate getter returns
  713. * null, a null value will be returned.
  714. *
  715. * @see NestedMethodProperty
  716. *
  717. * @param propertyId
  718. * @return true if the property was added
  719. */
  720. public boolean addNestedContainerProperty(String propertyId) {
  721. return addContainerProperty(propertyId,
  722. new NestedPropertyDescriptor(propertyId, type));
  723. }
  724. /**
  725. * Adds a nested container properties for all sub-properties of a named
  726. * property to the container. The named property itself is removed from the
  727. * model as its subproperties are added.
  728. *
  729. * All intermediate getters must exist and should return non-null values
  730. * when the property value is accessed. If an intermediate getter returns
  731. * null, a null value will be returned.
  732. *
  733. * @see NestedMethodProperty
  734. * @see #addNestedContainerProperty(String)
  735. *
  736. * @param propertyId
  737. */
  738. @SuppressWarnings("unchecked")
  739. public void addNestedContainerBean(String propertyId) {
  740. Class<?> propertyType = getType(propertyId);
  741. LinkedHashMap<String, VaadinPropertyDescriptor<Object>> pds = BeanItem
  742. .getPropertyDescriptors((Class<Object>) propertyType);
  743. for (String subPropertyId : pds.keySet()) {
  744. String qualifiedPropertyId = propertyId + "." + subPropertyId;
  745. NestedPropertyDescriptor<BEANTYPE> pd = new NestedPropertyDescriptor<BEANTYPE>(
  746. qualifiedPropertyId, (Class<BEANTYPE>) type);
  747. model.put(qualifiedPropertyId, pd);
  748. model.remove(propertyId);
  749. for (BeanItem<BEANTYPE> item : itemIdToItem.values()) {
  750. item.addItemProperty(qualifiedPropertyId,
  751. pd.createProperty(item.getBean()));
  752. item.removeItemProperty(propertyId);
  753. }
  754. }
  755. // Sends a change event
  756. fireContainerPropertySetChange();
  757. }
  758. @Override
  759. public boolean removeContainerProperty(Object propertyId)
  760. throws UnsupportedOperationException {
  761. // Fails if the Property is not present
  762. if (!model.containsKey(propertyId)) {
  763. return false;
  764. }
  765. // Removes the Property to Property list and types
  766. model.remove(propertyId);
  767. // If remove the Property from all Items
  768. for (final Iterator<IDTYPE> i = getAllItemIds().iterator(); i
  769. .hasNext();) {
  770. getUnfilteredItem(i.next()).removeItemProperty(propertyId);
  771. }
  772. // Sends a change event
  773. fireContainerPropertySetChange();
  774. return true;
  775. }
  776. }