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.

IndexedContainer.java 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  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.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.EventObject;
  22. import java.util.HashMap;
  23. import java.util.HashSet;
  24. import java.util.Hashtable;
  25. import java.util.Iterator;
  26. import java.util.LinkedList;
  27. import java.util.List;
  28. import java.util.Map;
  29. import com.vaadin.data.provider.ListDataProvider;
  30. import com.vaadin.v7.data.Container;
  31. import com.vaadin.v7.data.Item;
  32. import com.vaadin.v7.data.Property;
  33. import com.vaadin.v7.data.util.filter.SimpleStringFilter;
  34. import com.vaadin.v7.data.util.filter.UnsupportedFilterException;
  35. /**
  36. * An implementation of the <code>{@link Container.Indexed}</code> interface
  37. * with all important features.
  38. * <p>
  39. *
  40. * Features:
  41. * <ul>
  42. * <li>{@link Container.Indexed}
  43. * <li>{@link Container.Ordered}
  44. * <li>{@link Container.Sortable}
  45. * <li>{@link Container.Filterable}
  46. * <li>{@link Cloneable} (deprecated, might be removed in the future)
  47. * <li>Sends all needed events on content changes.
  48. * </ul>
  49. *
  50. * @see Container
  51. *
  52. * @author Vaadin Ltd.
  53. * @since 3.0
  54. *
  55. * @deprecated As of 8.0, replaced by {@link ListDataProvider}
  56. */
  57. @Deprecated
  58. @SuppressWarnings("serial")
  59. // item type is really IndexedContainerItem, but using Item not to show it in
  60. // public API
  61. public class IndexedContainer
  62. extends AbstractInMemoryContainer<Object, Object, Item>
  63. implements Container.PropertySetChangeNotifier,
  64. Property.ValueChangeNotifier, Container.Sortable, Cloneable,
  65. Container.Filterable, Container.SimpleFilterable {
  66. /* Internal structure */
  67. /**
  68. * Linked list of ordered Property IDs.
  69. */
  70. private ArrayList<Object> propertyIds = new ArrayList<Object>();
  71. /**
  72. * Property ID to type mapping.
  73. */
  74. private Hashtable<Object, Class<?>> types = new Hashtable<Object, Class<?>>();
  75. /**
  76. * Hash of Items, where each Item is implemented as a mapping from Property
  77. * ID to Property value.
  78. */
  79. private Hashtable<Object, Map<Object, Object>> items = new Hashtable<Object, Map<Object, Object>>();
  80. /**
  81. * Set of properties that are read-only.
  82. */
  83. private HashSet<Property<?>> readOnlyProperties = new HashSet<Property<?>>();
  84. /**
  85. * List of all Property value change event listeners listening all the
  86. * properties.
  87. */
  88. private LinkedList<Property.ValueChangeListener> propertyValueChangeListeners = null;
  89. /**
  90. * Data structure containing all listeners interested in changes to single
  91. * Properties. The data structure is a hashtable mapping Property IDs to a
  92. * hashtable that maps Item IDs to a linked list of listeners listening
  93. * Property identified by given Property ID and Item ID.
  94. */
  95. private Hashtable<Object, Map<Object, List<Property.ValueChangeListener>>> singlePropertyValueChangeListeners = null;
  96. private HashMap<Object, Object> defaultPropertyValues;
  97. private int nextGeneratedItemId = 1;
  98. /* Container constructors */
  99. public IndexedContainer() {
  100. super();
  101. }
  102. public IndexedContainer(Collection<?> itemIds) {
  103. this();
  104. if (items != null) {
  105. for (final Iterator<?> i = itemIds.iterator(); i.hasNext();) {
  106. Object itemId = i.next();
  107. internalAddItemAtEnd(itemId, new IndexedContainerItem(itemId),
  108. false);
  109. }
  110. filterAll();
  111. }
  112. }
  113. /* Container methods */
  114. @Override
  115. protected Item getUnfilteredItem(Object itemId) {
  116. if (itemId != null && items.containsKey(itemId)) {
  117. return new IndexedContainerItem(itemId);
  118. }
  119. return null;
  120. }
  121. @Override
  122. public Collection<?> getContainerPropertyIds() {
  123. return Collections.unmodifiableCollection(propertyIds);
  124. }
  125. /**
  126. * Gets the type of a Property stored in the list.
  127. *
  128. * @param propertyId
  129. * the ID of the Property.
  130. * @return Type of the requested Property
  131. */
  132. @Override
  133. public Class<?> getType(Object propertyId) {
  134. return types.get(propertyId);
  135. }
  136. @Override
  137. public Property getContainerProperty(Object itemId, Object propertyId) {
  138. // map lookup more efficient than propertyIds if there are many
  139. // properties
  140. if (!containsId(itemId) || propertyId == null
  141. || !types.containsKey(propertyId)) {
  142. return null;
  143. }
  144. return new IndexedContainerProperty(itemId, propertyId);
  145. }
  146. @Override
  147. public boolean addContainerProperty(Object propertyId, Class<?> type,
  148. Object defaultValue) {
  149. // Fails, if nulls are given
  150. if (propertyId == null || type == null) {
  151. return false;
  152. }
  153. // Fails if the Property is already present
  154. if (propertyIds.contains(propertyId)) {
  155. return false;
  156. }
  157. // Adds the Property to Property list and types
  158. propertyIds.add(propertyId);
  159. types.put(propertyId, type);
  160. // If default value is given, set it
  161. if (defaultValue != null) {
  162. // for existing rows
  163. for (final Iterator<?> i = getAllItemIds().iterator(); i
  164. .hasNext();) {
  165. getItem(i.next()).getItemProperty(propertyId)
  166. .setValue(defaultValue);
  167. }
  168. // store for next rows
  169. if (defaultPropertyValues == null) {
  170. defaultPropertyValues = new HashMap<Object, Object>();
  171. }
  172. defaultPropertyValues.put(propertyId, defaultValue);
  173. }
  174. // Sends a change event
  175. fireContainerPropertySetChange();
  176. return true;
  177. }
  178. @Override
  179. public boolean removeAllItems() {
  180. int origSize = size();
  181. Object firstItem = getFirstVisibleItem();
  182. internalRemoveAllItems();
  183. items.clear();
  184. // fire event only if the visible view changed, regardless of whether
  185. // filtered out items were removed or not
  186. if (origSize != 0) {
  187. // Sends a change event
  188. fireItemsRemoved(0, firstItem, origSize);
  189. }
  190. return true;
  191. }
  192. /**
  193. * {@inheritDoc}
  194. * <p>
  195. * The item ID is generated from a sequence of Integers. The id of the first
  196. * added item is 1.
  197. */
  198. @Override
  199. public Object addItem() {
  200. // Creates a new id
  201. final Object id = generateId();
  202. // Adds the Item into container
  203. addItem(id);
  204. return id;
  205. }
  206. @Override
  207. public Item addItem(Object itemId) {
  208. Item item = internalAddItemAtEnd(itemId,
  209. new IndexedContainerItem(itemId), false);
  210. if (item == null) {
  211. return null;
  212. } else if (!isFiltered()) {
  213. // always the last item
  214. fireItemAdded(size() - 1, itemId, item);
  215. } else if (passesFilters(itemId) && !containsId(itemId)) {
  216. getFilteredItemIds().add(itemId);
  217. // always the last item
  218. fireItemAdded(size() - 1, itemId, item);
  219. }
  220. return item;
  221. }
  222. /**
  223. * Helper method to add default values for items if available
  224. *
  225. * @param t
  226. * data table of added item
  227. */
  228. private void addDefaultValues(Hashtable<Object, Object> t) {
  229. if (defaultPropertyValues != null) {
  230. for (Object key : defaultPropertyValues.keySet()) {
  231. t.put(key, defaultPropertyValues.get(key));
  232. }
  233. }
  234. }
  235. @Override
  236. public boolean removeItem(Object itemId) {
  237. if (itemId == null || items.remove(itemId) == null) {
  238. return false;
  239. }
  240. int origSize = size();
  241. int position = indexOfId(itemId);
  242. if (internalRemoveItem(itemId)) {
  243. // fire event only if the visible view changed, regardless of
  244. // whether filtered out items were removed or not
  245. if (size() != origSize) {
  246. fireItemRemoved(position, itemId);
  247. }
  248. return true;
  249. } else {
  250. return false;
  251. }
  252. }
  253. @Override
  254. public boolean removeContainerProperty(Object propertyId) {
  255. // Fails if the Property is not present
  256. if (!propertyIds.contains(propertyId)) {
  257. return false;
  258. }
  259. // Removes the Property to Property list and types
  260. propertyIds.remove(propertyId);
  261. types.remove(propertyId);
  262. if (defaultPropertyValues != null) {
  263. defaultPropertyValues.remove(propertyId);
  264. }
  265. // If remove the Property from all Items
  266. for (final Iterator<Object> i = getAllItemIds().iterator(); i
  267. .hasNext();) {
  268. items.get(i.next()).remove(propertyId);
  269. }
  270. // Sends a change event
  271. fireContainerPropertySetChange();
  272. return true;
  273. }
  274. /* Container.Ordered methods */
  275. @Override
  276. public Item addItemAfter(Object previousItemId, Object newItemId) {
  277. return internalAddItemAfter(previousItemId, newItemId,
  278. new IndexedContainerItem(newItemId), true);
  279. }
  280. /**
  281. * {@inheritDoc}
  282. * <p>
  283. * The item ID is generated from a sequence of Integers. The id of the first
  284. * added item is 1.
  285. */
  286. @Override
  287. public Object addItemAfter(Object previousItemId) {
  288. // Creates a new id
  289. final Object id = generateId();
  290. if (addItemAfter(previousItemId, id) != null) {
  291. return id;
  292. } else {
  293. return null;
  294. }
  295. }
  296. @Override
  297. public Item addItemAt(int index, Object newItemId) {
  298. return internalAddItemAt(index, newItemId,
  299. new IndexedContainerItem(newItemId), true);
  300. }
  301. /**
  302. * {@inheritDoc}
  303. * <p>
  304. * The item ID is generated from a sequence of Integers. The id of the first
  305. * added item is 1.
  306. */
  307. @Override
  308. public Object addItemAt(int index) {
  309. // Creates a new id
  310. final Object id = generateId();
  311. // Adds the Item into container
  312. addItemAt(index, id);
  313. return id;
  314. }
  315. /**
  316. * Generates an unique identifier for use as an item id. Guarantees that the
  317. * generated id is not currently used as an id.
  318. *
  319. * @return
  320. */
  321. private Serializable generateId() {
  322. Serializable id;
  323. do {
  324. id = Integer.valueOf(nextGeneratedItemId++);
  325. } while (items.containsKey(id));
  326. return id;
  327. }
  328. @Override
  329. protected void registerNewItem(int index, Object newItemId, Item item) {
  330. Hashtable<Object, Object> t = new Hashtable<Object, Object>();
  331. items.put(newItemId, t);
  332. addDefaultValues(t);
  333. }
  334. /* Event notifiers */
  335. /**
  336. * An <code>event</code> object specifying the list whose Item set has
  337. * changed.
  338. *
  339. * @author Vaadin Ltd.
  340. * @since 3.0
  341. */
  342. @Deprecated
  343. public static class ItemSetChangeEvent extends BaseItemSetChangeEvent {
  344. private final int addedItemIndex;
  345. private ItemSetChangeEvent(IndexedContainer source,
  346. int addedItemIndex) {
  347. super(source);
  348. this.addedItemIndex = addedItemIndex;
  349. }
  350. /**
  351. * If and only if one item is added, gives its index.
  352. *
  353. * @return -1 if either multiple items are changed or some other change
  354. * than add is done.
  355. */
  356. public int getAddedItemIndex() {
  357. return addedItemIndex;
  358. }
  359. }
  360. /**
  361. * An <code>event</code> object specifying the Property in a list whose
  362. * value has changed.
  363. *
  364. * @author Vaadin Ltd.
  365. * @since 3.0
  366. */
  367. private static class PropertyValueChangeEvent extends EventObject
  368. implements Property.ValueChangeEvent, Serializable {
  369. private PropertyValueChangeEvent(Property source) {
  370. super(source);
  371. }
  372. @Override
  373. public Property getProperty() {
  374. return (Property) getSource();
  375. }
  376. }
  377. @Override
  378. public void addPropertySetChangeListener(
  379. Container.PropertySetChangeListener listener) {
  380. super.addPropertySetChangeListener(listener);
  381. }
  382. /**
  383. * @deprecated As of 7.0, replaced by
  384. * {@link #addPropertySetChangeListener(Container.PropertySetChangeListener)}
  385. **/
  386. @Deprecated
  387. @Override
  388. public void addListener(Container.PropertySetChangeListener listener) {
  389. addPropertySetChangeListener(listener);
  390. }
  391. @Override
  392. public void removePropertySetChangeListener(
  393. Container.PropertySetChangeListener listener) {
  394. super.removePropertySetChangeListener(listener);
  395. }
  396. /**
  397. * @deprecated As of 7.0, replaced by
  398. * {@link #removePropertySetChangeListener(Container.PropertySetChangeListener)}
  399. **/
  400. @Deprecated
  401. @Override
  402. public void removeListener(Container.PropertySetChangeListener listener) {
  403. removePropertySetChangeListener(listener);
  404. }
  405. @Override
  406. public void addValueChangeListener(Property.ValueChangeListener listener) {
  407. if (propertyValueChangeListeners == null) {
  408. propertyValueChangeListeners = new LinkedList<Property.ValueChangeListener>();
  409. }
  410. propertyValueChangeListeners.add(listener);
  411. }
  412. /**
  413. * @deprecated As of 7.0, replaced by
  414. * {@link #addValueChangeListener(Property.ValueChangeListener)}
  415. **/
  416. @Override
  417. @Deprecated
  418. public void addListener(Property.ValueChangeListener listener) {
  419. addValueChangeListener(listener);
  420. }
  421. @Override
  422. public void removeValueChangeListener(
  423. Property.ValueChangeListener listener) {
  424. if (propertyValueChangeListeners != null) {
  425. propertyValueChangeListeners.remove(listener);
  426. }
  427. }
  428. /**
  429. * @deprecated As of 7.0, replaced by
  430. * {@link #removeValueChangeListener(Property.ValueChangeListener)}
  431. **/
  432. @Override
  433. @Deprecated
  434. public void removeListener(Property.ValueChangeListener listener) {
  435. removeValueChangeListener(listener);
  436. }
  437. /**
  438. * Sends a Property value change event to all interested listeners.
  439. *
  440. * @param source
  441. * the IndexedContainerProperty object.
  442. */
  443. private void firePropertyValueChange(IndexedContainerProperty source) {
  444. // Sends event to listeners listening all value changes
  445. if (propertyValueChangeListeners != null) {
  446. final Object[] l = propertyValueChangeListeners.toArray();
  447. final Property.ValueChangeEvent event = new IndexedContainer.PropertyValueChangeEvent(
  448. source);
  449. for (int i = 0; i < l.length; i++) {
  450. ((Property.ValueChangeListener) l[i]).valueChange(event);
  451. }
  452. }
  453. // Sends event to single property value change listeners
  454. if (singlePropertyValueChangeListeners != null) {
  455. final Map<Object, List<Property.ValueChangeListener>> propertySetToListenerListMap = singlePropertyValueChangeListeners
  456. .get(source.propertyId);
  457. if (propertySetToListenerListMap != null) {
  458. final List<Property.ValueChangeListener> listenerList = propertySetToListenerListMap
  459. .get(source.itemId);
  460. if (listenerList != null) {
  461. final Property.ValueChangeEvent event = new IndexedContainer.PropertyValueChangeEvent(
  462. source);
  463. Object[] listeners = listenerList.toArray();
  464. for (int i = 0; i < listeners.length; i++) {
  465. ((Property.ValueChangeListener) listeners[i])
  466. .valueChange(event);
  467. }
  468. }
  469. }
  470. }
  471. }
  472. @Override
  473. public Collection<?> getListeners(Class<?> eventType) {
  474. if (Property.ValueChangeEvent.class.isAssignableFrom(eventType)) {
  475. if (propertyValueChangeListeners == null) {
  476. return Collections.EMPTY_LIST;
  477. } else {
  478. return Collections
  479. .unmodifiableCollection(propertyValueChangeListeners);
  480. }
  481. }
  482. return super.getListeners(eventType);
  483. }
  484. @Override
  485. protected void fireItemAdded(int position, Object itemId, Item item) {
  486. if (position >= 0) {
  487. super.fireItemAdded(position, itemId, item);
  488. }
  489. }
  490. @Override
  491. protected void fireItemSetChange() {
  492. fireItemSetChange(new IndexedContainer.ItemSetChangeEvent(this, -1));
  493. }
  494. /**
  495. * Adds new single Property change listener.
  496. *
  497. * @param propertyId
  498. * the ID of the Property to add.
  499. * @param itemId
  500. * the ID of the Item .
  501. * @param listener
  502. * the listener to be added.
  503. */
  504. private void addSinglePropertyChangeListener(Object propertyId,
  505. Object itemId, Property.ValueChangeListener listener) {
  506. if (listener != null) {
  507. if (singlePropertyValueChangeListeners == null) {
  508. singlePropertyValueChangeListeners = new Hashtable<Object, Map<Object, List<Property.ValueChangeListener>>>();
  509. }
  510. Map<Object, List<Property.ValueChangeListener>> propertySetToListenerListMap = singlePropertyValueChangeListeners
  511. .get(propertyId);
  512. if (propertySetToListenerListMap == null) {
  513. propertySetToListenerListMap = new Hashtable<Object, List<Property.ValueChangeListener>>();
  514. singlePropertyValueChangeListeners.put(propertyId,
  515. propertySetToListenerListMap);
  516. }
  517. List<Property.ValueChangeListener> listenerList = propertySetToListenerListMap
  518. .get(itemId);
  519. if (listenerList == null) {
  520. listenerList = new LinkedList<Property.ValueChangeListener>();
  521. propertySetToListenerListMap.put(itemId, listenerList);
  522. }
  523. listenerList.add(listener);
  524. }
  525. }
  526. /**
  527. * Removes a previously registered single Property change listener.
  528. *
  529. * @param propertyId
  530. * the ID of the Property to remove.
  531. * @param itemId
  532. * the ID of the Item.
  533. * @param listener
  534. * the listener to be removed.
  535. */
  536. private void removeSinglePropertyChangeListener(Object propertyId,
  537. Object itemId, Property.ValueChangeListener listener) {
  538. if (listener != null && singlePropertyValueChangeListeners != null) {
  539. final Map<Object, List<Property.ValueChangeListener>> propertySetToListenerListMap = singlePropertyValueChangeListeners
  540. .get(propertyId);
  541. if (propertySetToListenerListMap != null) {
  542. final List<Property.ValueChangeListener> listenerList = propertySetToListenerListMap
  543. .get(itemId);
  544. if (listenerList != null) {
  545. listenerList.remove(listener);
  546. if (listenerList.isEmpty()) {
  547. propertySetToListenerListMap.remove(itemId);
  548. }
  549. }
  550. if (propertySetToListenerListMap.isEmpty()) {
  551. singlePropertyValueChangeListeners.remove(propertyId);
  552. }
  553. }
  554. if (singlePropertyValueChangeListeners.isEmpty()) {
  555. singlePropertyValueChangeListeners = null;
  556. }
  557. }
  558. }
  559. /* Internal Item and Property implementations */
  560. /**
  561. * A class implementing the {@link Item} interface to be contained in the
  562. * list.
  563. *
  564. * @author Vaadin Ltd.
  565. *
  566. *
  567. * @since 3.0
  568. */
  569. @Deprecated
  570. class IndexedContainerItem implements Item {
  571. /**
  572. * Item ID in the host container for this Item.
  573. */
  574. private final Object itemId;
  575. /**
  576. * Constructs a new ListItem instance and connects it to a host
  577. * container.
  578. *
  579. * @param itemId
  580. * the Item ID of the new Item.
  581. */
  582. private IndexedContainerItem(Object itemId) {
  583. this.itemId = itemId;
  584. }
  585. @Override
  586. public Property getItemProperty(Object id) {
  587. if (!propertyIds.contains(id)) {
  588. return null;
  589. }
  590. return new IndexedContainerProperty(itemId, id);
  591. }
  592. @Override
  593. public Collection<?> getItemPropertyIds() {
  594. return Collections.unmodifiableCollection(propertyIds);
  595. }
  596. /**
  597. * Gets the <code>String</code> representation of the contents of the
  598. * Item. The format of the string is a space separated catenation of the
  599. * <code>String</code> representations of the values of the Properties
  600. * contained by the Item.
  601. *
  602. * @return <code>String</code> representation of the Item contents
  603. */
  604. @Override
  605. public String toString() {
  606. String retValue = "";
  607. for (final Iterator<?> i = propertyIds.iterator(); i.hasNext();) {
  608. final Object propertyId = i.next();
  609. retValue += getItemProperty(propertyId).getValue();
  610. if (i.hasNext()) {
  611. retValue += " ";
  612. }
  613. }
  614. return retValue;
  615. }
  616. /**
  617. * Calculates a integer hash-code for the Item that's unique inside the
  618. * list. Two Items inside the same list have always different
  619. * hash-codes, though Items in different lists may have identical
  620. * hash-codes.
  621. *
  622. * @return A locally unique hash-code as integer
  623. */
  624. @Override
  625. public int hashCode() {
  626. return itemId.hashCode();
  627. }
  628. /**
  629. * Tests if the given object is the same as the this object. Two Items
  630. * got from a list container with the same ID are equal.
  631. *
  632. * @param obj
  633. * an object to compare with this object
  634. * @return <code>true</code> if the given object is the same as this
  635. * object, <code>false</code> if not
  636. */
  637. @Override
  638. public boolean equals(Object obj) {
  639. if (obj == null
  640. || !obj.getClass().equals(IndexedContainerItem.class)) {
  641. return false;
  642. }
  643. final IndexedContainerItem li = (IndexedContainerItem) obj;
  644. return getHost() == li.getHost() && itemId.equals(li.itemId);
  645. }
  646. private IndexedContainer getHost() {
  647. return IndexedContainer.this;
  648. }
  649. /**
  650. * IndexedContainerItem does not support adding new properties. Add
  651. * properties at container level. See
  652. * {@link IndexedContainer#addContainerProperty(Object, Class, Object)}
  653. *
  654. * @see Item#addProperty(Object, Property)
  655. */
  656. @Override
  657. public boolean addItemProperty(Object id, Property property)
  658. throws UnsupportedOperationException {
  659. throw new UnsupportedOperationException("Indexed container item "
  660. + "does not support adding new properties");
  661. }
  662. /**
  663. * Indexed container does not support removing properties. Remove
  664. * properties at container level. See
  665. * {@link IndexedContainer#removeContainerProperty(Object)}
  666. *
  667. * @see Item#removeProperty(Object)
  668. */
  669. @Override
  670. public boolean removeItemProperty(Object id)
  671. throws UnsupportedOperationException {
  672. throw new UnsupportedOperationException(
  673. "Indexed container item does not support property removal");
  674. }
  675. }
  676. /**
  677. * A class implementing the {@link Property} interface to be contained in
  678. * the {@link IndexedContainerItem} contained in the
  679. * {@link IndexedContainer}.
  680. *
  681. * @author Vaadin Ltd.
  682. *
  683. * @since 3.0
  684. */
  685. private class IndexedContainerProperty<T>
  686. implements Property<T>, Property.ValueChangeNotifier {
  687. /**
  688. * ID of the Item, where this property resides.
  689. */
  690. private final Object itemId;
  691. /**
  692. * Id of the Property.
  693. */
  694. private final Object propertyId;
  695. /**
  696. * Constructs a new {@link IndexedContainerProperty} object.
  697. *
  698. * @param itemId
  699. * the ID of the Item to connect the new Property to.
  700. * @param propertyId
  701. * the Property ID of the new Property.
  702. * @param host
  703. * the list that contains the Item to contain the new
  704. * Property.
  705. */
  706. private IndexedContainerProperty(Object itemId, Object propertyId) {
  707. if (itemId == null || propertyId == null) {
  708. // Null ids are not accepted
  709. throw new NullPointerException(
  710. "Container item or property ids can not be null");
  711. }
  712. this.propertyId = propertyId;
  713. this.itemId = itemId;
  714. }
  715. @Override
  716. public Class<T> getType() {
  717. return (Class<T>) types.get(propertyId);
  718. }
  719. @Override
  720. public T getValue() {
  721. return (T) items.get(itemId).get(propertyId);
  722. }
  723. @Override
  724. public boolean isReadOnly() {
  725. return readOnlyProperties.contains(this);
  726. }
  727. @Override
  728. public void setReadOnly(boolean newStatus) {
  729. if (newStatus) {
  730. readOnlyProperties.add(this);
  731. } else {
  732. readOnlyProperties.remove(this);
  733. }
  734. }
  735. @Override
  736. public void setValue(Object newValue)
  737. throws Property.ReadOnlyException {
  738. // Gets the Property set
  739. final Map<Object, Object> propertySet = items.get(itemId);
  740. // Support null values on all types
  741. if (newValue == null) {
  742. propertySet.remove(propertyId);
  743. } else if (getType().isAssignableFrom(newValue.getClass())) {
  744. propertySet.put(propertyId, newValue);
  745. } else {
  746. throw new IllegalArgumentException(
  747. "Value is of invalid type, got "
  748. + newValue.getClass().getName() + " but "
  749. + getType().getName() + " was expected");
  750. }
  751. // update the container filtering if this property is being filtered
  752. if (isPropertyFiltered(propertyId)) {
  753. filterAll();
  754. }
  755. firePropertyValueChange(this);
  756. }
  757. // LegacyPropertyHelper has been removed in Vaadin 8
  758. /**
  759. * Calculates a integer hash-code for the Property that's unique inside
  760. * the Item containing the Property. Two different Properties inside the
  761. * same Item contained in the same list always have different
  762. * hash-codes, though Properties in different Items may have identical
  763. * hash-codes.
  764. *
  765. * @return A locally unique hash-code as integer
  766. */
  767. @Override
  768. public int hashCode() {
  769. return itemId.hashCode() ^ propertyId.hashCode();
  770. }
  771. /**
  772. * Tests if the given object is the same as the this object. Two
  773. * Properties got from an Item with the same ID are equal.
  774. *
  775. * @param obj
  776. * an object to compare with this object
  777. * @return <code>true</code> if the given object is the same as this
  778. * object, <code>false</code> if not
  779. */
  780. @Override
  781. public boolean equals(Object obj) {
  782. if (obj == null
  783. || !obj.getClass().equals(IndexedContainerProperty.class)) {
  784. return false;
  785. }
  786. final IndexedContainerProperty lp = (IndexedContainerProperty) obj;
  787. return lp.getHost() == getHost() && lp.propertyId.equals(propertyId)
  788. && lp.itemId.equals(itemId);
  789. }
  790. @Override
  791. public void addValueChangeListener(
  792. Property.ValueChangeListener listener) {
  793. addSinglePropertyChangeListener(propertyId, itemId, listener);
  794. }
  795. /**
  796. * @deprecated As of 7.0, replaced by
  797. * {@link #addValueChangeListener(Property.ValueChangeListener)}
  798. **/
  799. @Override
  800. @Deprecated
  801. public void addListener(Property.ValueChangeListener listener) {
  802. addValueChangeListener(listener);
  803. }
  804. @Override
  805. public void removeValueChangeListener(
  806. Property.ValueChangeListener listener) {
  807. removeSinglePropertyChangeListener(propertyId, itemId, listener);
  808. }
  809. /**
  810. * @deprecated As of 7.0, replaced by
  811. * {@link #removeValueChangeListener(Property.ValueChangeListener)}
  812. **/
  813. @Override
  814. @Deprecated
  815. public void removeListener(Property.ValueChangeListener listener) {
  816. removeValueChangeListener(listener);
  817. }
  818. private IndexedContainer getHost() {
  819. return IndexedContainer.this;
  820. }
  821. }
  822. @Override
  823. public void sort(Object[] propertyId, boolean[] ascending) {
  824. sortContainer(propertyId, ascending);
  825. }
  826. @Override
  827. public Collection<?> getSortableContainerPropertyIds() {
  828. return getSortablePropertyIds();
  829. }
  830. @Override
  831. public ItemSorter getItemSorter() {
  832. return super.getItemSorter();
  833. }
  834. @Override
  835. public void setItemSorter(ItemSorter itemSorter) {
  836. super.setItemSorter(itemSorter);
  837. }
  838. /**
  839. * Supports cloning of the IndexedContainer cleanly.
  840. *
  841. * @throws CloneNotSupportedException
  842. * if an object cannot be cloned. .
  843. *
  844. * @deprecated As of 6.6. Cloning support might be removed from
  845. * IndexedContainer in the future
  846. */
  847. @Deprecated
  848. @Override
  849. public Object clone() throws CloneNotSupportedException {
  850. // Creates the clone
  851. final IndexedContainer nc = new IndexedContainer();
  852. // Clone the shallow properties
  853. nc.setAllItemIds(getAllItemIds() != null
  854. ? (ListSet<Object>) ((ListSet<Object>) getAllItemIds()).clone()
  855. : null);
  856. nc.setItemSetChangeListeners(getItemSetChangeListeners() != null
  857. ? new LinkedList<Container.ItemSetChangeListener>(
  858. getItemSetChangeListeners())
  859. : null);
  860. nc.propertyIds = propertyIds != null
  861. ? (ArrayList<Object>) propertyIds.clone() : null;
  862. nc.setPropertySetChangeListeners(getPropertySetChangeListeners() != null
  863. ? new LinkedList<Container.PropertySetChangeListener>(
  864. getPropertySetChangeListeners())
  865. : null);
  866. nc.propertyValueChangeListeners = propertyValueChangeListeners != null
  867. ? (LinkedList<Property.ValueChangeListener>) propertyValueChangeListeners
  868. .clone()
  869. : null;
  870. nc.readOnlyProperties = readOnlyProperties != null
  871. ? (HashSet<Property<?>>) readOnlyProperties.clone() : null;
  872. nc.singlePropertyValueChangeListeners = singlePropertyValueChangeListeners != null
  873. ? (Hashtable<Object, Map<Object, List<Property.ValueChangeListener>>>) singlePropertyValueChangeListeners
  874. .clone()
  875. : null;
  876. nc.types = types != null ? (Hashtable<Object, Class<?>>) types.clone()
  877. : null;
  878. nc.setFilters(
  879. (HashSet<Filter>) ((HashSet<Filter>) getFilters()).clone());
  880. nc.setFilteredItemIds(getFilteredItemIds() == null ? null
  881. : (ListSet<Object>) ((ListSet<Object>) getFilteredItemIds())
  882. .clone());
  883. // Clone property-values
  884. if (items == null) {
  885. nc.items = null;
  886. } else {
  887. nc.items = new Hashtable<Object, Map<Object, Object>>();
  888. for (final Iterator<?> i = items.keySet().iterator(); i
  889. .hasNext();) {
  890. final Object id = i.next();
  891. final Hashtable<Object, Object> it = (Hashtable<Object, Object>) items
  892. .get(id);
  893. nc.items.put(id, (Map<Object, Object>) it.clone());
  894. }
  895. }
  896. return nc;
  897. }
  898. @Override
  899. public void addContainerFilter(Object propertyId, String filterString,
  900. boolean ignoreCase, boolean onlyMatchPrefix) {
  901. try {
  902. addFilter(new SimpleStringFilter(propertyId, filterString,
  903. ignoreCase, onlyMatchPrefix));
  904. } catch (UnsupportedFilterException e) {
  905. // the filter instance created here is always valid for in-memory
  906. // containers
  907. }
  908. }
  909. @Override
  910. public void removeAllContainerFilters() {
  911. removeAllFilters();
  912. }
  913. @Override
  914. public void removeContainerFilters(Object propertyId) {
  915. removeFilters(propertyId);
  916. }
  917. @Override
  918. public void addContainerFilter(Filter filter)
  919. throws UnsupportedFilterException {
  920. addFilter(filter);
  921. }
  922. @Override
  923. public void removeContainerFilter(Filter filter) {
  924. removeFilter(filter);
  925. }
  926. @Override
  927. public boolean hasContainerFilters() {
  928. return super.hasContainerFilters();
  929. }
  930. @Override
  931. public Collection<Filter> getContainerFilters() {
  932. return super.getContainerFilters();
  933. }
  934. }