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.

AbstractSelect.java 76KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344
  1. /*
  2. * Copyright 2000-2014 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.ui;
  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.Iterator;
  25. import java.util.LinkedHashSet;
  26. import java.util.LinkedList;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Set;
  30. import org.jsoup.nodes.Element;
  31. import com.vaadin.data.Container;
  32. import com.vaadin.data.Item;
  33. import com.vaadin.data.Property;
  34. import com.vaadin.data.Validator.InvalidValueException;
  35. import com.vaadin.data.util.IndexedContainer;
  36. import com.vaadin.data.util.converter.Converter;
  37. import com.vaadin.data.util.converter.Converter.ConversionException;
  38. import com.vaadin.data.util.converter.ConverterUtil;
  39. import com.vaadin.event.DataBoundTransferable;
  40. import com.vaadin.event.Transferable;
  41. import com.vaadin.event.dd.DragAndDropEvent;
  42. import com.vaadin.event.dd.DropTarget;
  43. import com.vaadin.event.dd.TargetDetailsImpl;
  44. import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion;
  45. import com.vaadin.event.dd.acceptcriteria.ContainsDataFlavor;
  46. import com.vaadin.event.dd.acceptcriteria.TargetDetailIs;
  47. import com.vaadin.server.KeyMapper;
  48. import com.vaadin.server.PaintException;
  49. import com.vaadin.server.PaintTarget;
  50. import com.vaadin.server.Resource;
  51. import com.vaadin.server.VaadinSession;
  52. import com.vaadin.shared.ui.combobox.FilteringMode;
  53. import com.vaadin.shared.ui.dd.VerticalDropLocation;
  54. import com.vaadin.shared.ui.select.AbstractSelectState;
  55. import com.vaadin.ui.declarative.DesignAttributeHandler;
  56. import com.vaadin.ui.declarative.DesignContext;
  57. import com.vaadin.ui.declarative.DesignException;
  58. import com.vaadin.ui.declarative.DesignFormatter;
  59. /**
  60. * <p>
  61. * A class representing a selection of items the user has selected in a UI. The
  62. * set of choices is presented as a set of {@link com.vaadin.data.Item}s in a
  63. * {@link com.vaadin.data.Container}.
  64. * </p>
  65. *
  66. * <p>
  67. * A <code>Select</code> component may be in single- or multiselect mode.
  68. * Multiselect mode means that more than one item can be selected
  69. * simultaneously.
  70. * </p>
  71. *
  72. * @author Vaadin Ltd.
  73. * @since 5.0
  74. */
  75. @SuppressWarnings("serial")
  76. // TODO currently cannot specify type more precisely in case of multi-select
  77. public abstract class AbstractSelect extends AbstractField<Object> implements
  78. Container, Container.Viewer, Container.PropertySetChangeListener,
  79. Container.PropertySetChangeNotifier, Container.ItemSetChangeNotifier,
  80. Container.ItemSetChangeListener, LegacyComponent {
  81. public enum ItemCaptionMode {
  82. /**
  83. * Item caption mode: Item's ID converted to a String using
  84. * {@link VaadinSession#getConverterFactory()} is used as caption.
  85. */
  86. ID,
  87. /**
  88. * Item caption mode: Item's ID's <code>String</code> representation is
  89. * used as caption.
  90. *
  91. * @since 7.5.6
  92. */
  93. ID_TOSTRING,
  94. /**
  95. * Item caption mode: Item's <code>String</code> representation is used
  96. * as caption.
  97. */
  98. ITEM,
  99. /**
  100. * Item caption mode: Index of the item is used as caption. The index
  101. * mode can only be used with the containers implementing the
  102. * {@link com.vaadin.data.Container.Indexed} interface.
  103. */
  104. INDEX,
  105. /**
  106. * Item caption mode: If an Item has a caption it's used, if not, Item's
  107. * ID converted to a String using
  108. * {@link VaadinSession#getConverterFactory()} is used as caption.
  109. * <b>This is the default</b>.
  110. */
  111. EXPLICIT_DEFAULTS_ID,
  112. /**
  113. * Item caption mode: Captions must be explicitly specified.
  114. */
  115. EXPLICIT,
  116. /**
  117. * Item caption mode: Only icons are shown, captions are hidden.
  118. */
  119. ICON_ONLY,
  120. /**
  121. * Item caption mode: Item captions are read from property specified
  122. * with <code>setItemCaptionPropertyId</code>.
  123. */
  124. PROPERTY;
  125. }
  126. /**
  127. * @deprecated As of 7.0, use {@link ItemCaptionMode#ID} instead
  128. */
  129. @Deprecated
  130. public static final ItemCaptionMode ITEM_CAPTION_MODE_ID = ItemCaptionMode.ID;
  131. /**
  132. * @deprecated As of 7.0, use {@link ItemCaptionMode#ITEM} instead
  133. */
  134. @Deprecated
  135. public static final ItemCaptionMode ITEM_CAPTION_MODE_ITEM = ItemCaptionMode.ITEM;
  136. /**
  137. * @deprecated As of 7.0, use {@link ItemCaptionMode#INDEX} instead
  138. */
  139. @Deprecated
  140. public static final ItemCaptionMode ITEM_CAPTION_MODE_INDEX = ItemCaptionMode.INDEX;
  141. /**
  142. * @deprecated As of 7.0, use {@link ItemCaptionMode#EXPLICIT_DEFAULTS_ID}
  143. * instead
  144. */
  145. @Deprecated
  146. public static final ItemCaptionMode ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID = ItemCaptionMode.EXPLICIT_DEFAULTS_ID;
  147. /**
  148. * @deprecated As of 7.0, use {@link ItemCaptionMode#EXPLICIT} instead
  149. */
  150. @Deprecated
  151. public static final ItemCaptionMode ITEM_CAPTION_MODE_EXPLICIT = ItemCaptionMode.EXPLICIT;
  152. /**
  153. * @deprecated As of 7.0, use {@link ItemCaptionMode#ICON_ONLY} instead
  154. */
  155. @Deprecated
  156. public static final ItemCaptionMode ITEM_CAPTION_MODE_ICON_ONLY = ItemCaptionMode.ICON_ONLY;
  157. /**
  158. * @deprecated As of 7.0, use {@link ItemCaptionMode#PROPERTY} instead
  159. */
  160. @Deprecated
  161. public static final ItemCaptionMode ITEM_CAPTION_MODE_PROPERTY = ItemCaptionMode.PROPERTY;
  162. /**
  163. * Interface for option filtering, used to filter options based on user
  164. * entered value. The value is matched to the item caption.
  165. * <code>FilteringMode.OFF</code> (0) turns the filtering off.
  166. * <code>FilteringMode.STARTSWITH</code> (1) matches from the start of the
  167. * caption. <code>FilteringMode.CONTAINS</code> (1) matches anywhere in the
  168. * caption.
  169. */
  170. public interface Filtering extends Serializable {
  171. /**
  172. * @deprecated As of 7.0, use {@link FilteringMode#OFF} instead
  173. */
  174. @Deprecated
  175. public static final FilteringMode FILTERINGMODE_OFF = FilteringMode.OFF;
  176. /**
  177. * @deprecated As of 7.0, use {@link FilteringMode#STARTSWITH} instead
  178. */
  179. @Deprecated
  180. public static final FilteringMode FILTERINGMODE_STARTSWITH = FilteringMode.STARTSWITH;
  181. /**
  182. * @deprecated As of 7.0, use {@link FilteringMode#CONTAINS} instead
  183. */
  184. @Deprecated
  185. public static final FilteringMode FILTERINGMODE_CONTAINS = FilteringMode.CONTAINS;
  186. /**
  187. * Sets the option filtering mode.
  188. *
  189. * @param filteringMode
  190. * the filtering mode to use
  191. */
  192. public void setFilteringMode(FilteringMode filteringMode);
  193. /**
  194. * Gets the current filtering mode.
  195. *
  196. * @return the filtering mode in use
  197. */
  198. public FilteringMode getFilteringMode();
  199. }
  200. /**
  201. * Is the select in multiselect mode?
  202. */
  203. private boolean multiSelect = false;
  204. /**
  205. * Select options.
  206. */
  207. protected Container items;
  208. /**
  209. * Is the user allowed to add new options?
  210. */
  211. private boolean allowNewOptions;
  212. /**
  213. * Keymapper used to map key values.
  214. */
  215. protected KeyMapper<Object> itemIdMapper = new KeyMapper<Object>();
  216. /**
  217. * Item icons.
  218. */
  219. private final HashMap<Object, Resource> itemIcons = new HashMap<Object, Resource>();
  220. /**
  221. * Item captions.
  222. */
  223. private final HashMap<Object, String> itemCaptions = new HashMap<Object, String>();
  224. /**
  225. * Item caption mode.
  226. */
  227. private ItemCaptionMode itemCaptionMode = ItemCaptionMode.EXPLICIT_DEFAULTS_ID;
  228. /**
  229. * Item caption source property id.
  230. */
  231. private Object itemCaptionPropertyId = null;
  232. /**
  233. * Item icon source property id.
  234. */
  235. private Object itemIconPropertyId = null;
  236. /**
  237. * List of property set change event listeners.
  238. */
  239. private Set<Container.PropertySetChangeListener> propertySetEventListeners = null;
  240. /**
  241. * List of item set change event listeners.
  242. */
  243. private Set<Container.ItemSetChangeListener> itemSetEventListeners = null;
  244. /**
  245. * Item id that represents null selection of this select.
  246. *
  247. * <p>
  248. * Data interface does not support nulls as item ids. Selecting the item
  249. * identified by this id is the same as selecting no items at all. This
  250. * setting only affects the single select mode.
  251. * </p>
  252. */
  253. private Object nullSelectionItemId = null;
  254. // Null (empty) selection is enabled by default
  255. private boolean nullSelectionAllowed = true;
  256. private NewItemHandler newItemHandler;
  257. // Caption (Item / Property) change listeners
  258. CaptionChangeListener captionChangeListener;
  259. /* Constructors */
  260. /**
  261. * Creates an empty Select. The caption is not used.
  262. */
  263. public AbstractSelect() {
  264. setContainerDataSource(new IndexedContainer());
  265. }
  266. /**
  267. * Creates an empty Select with caption.
  268. */
  269. public AbstractSelect(String caption) {
  270. setContainerDataSource(new IndexedContainer());
  271. setCaption(caption);
  272. }
  273. /**
  274. * Creates a new select that is connected to a data-source.
  275. *
  276. * @param caption
  277. * the Caption of the component.
  278. * @param dataSource
  279. * the Container datasource to be selected from by this select.
  280. */
  281. public AbstractSelect(String caption, Container dataSource) {
  282. setCaption(caption);
  283. setContainerDataSource(dataSource);
  284. }
  285. /**
  286. * Creates a new select that is filled from a collection of option values.
  287. *
  288. * @param caption
  289. * the Caption of this field.
  290. * @param options
  291. * the Collection containing the options.
  292. */
  293. public AbstractSelect(String caption, Collection<?> options) {
  294. // Creates the options container and add given options to it
  295. final Container c = new IndexedContainer();
  296. if (options != null) {
  297. for (final Iterator<?> i = options.iterator(); i.hasNext();) {
  298. c.addItem(i.next());
  299. }
  300. }
  301. setCaption(caption);
  302. setContainerDataSource(c);
  303. }
  304. /* Component methods */
  305. /**
  306. * Paints the content of this component.
  307. *
  308. * @param target
  309. * the Paint Event.
  310. * @throws PaintException
  311. * if the paint operation failed.
  312. */
  313. @Override
  314. public void paintContent(PaintTarget target) throws PaintException {
  315. // Paints select attributes
  316. if (isMultiSelect()) {
  317. target.addAttribute("selectmode", "multi");
  318. }
  319. if (isNewItemsAllowed()) {
  320. target.addAttribute("allownewitem", true);
  321. }
  322. if (isNullSelectionAllowed()) {
  323. target.addAttribute("nullselect", true);
  324. if (getNullSelectionItemId() != null) {
  325. target.addAttribute("nullselectitem", true);
  326. }
  327. }
  328. // Constructs selected keys array
  329. String[] selectedKeys;
  330. if (isMultiSelect()) {
  331. selectedKeys = new String[((Set<?>) getValue()).size()];
  332. } else {
  333. selectedKeys = new String[(getValue() == null
  334. && getNullSelectionItemId() == null ? 0 : 1)];
  335. }
  336. // ==
  337. // first remove all previous item/property listeners
  338. getCaptionChangeListener().clear();
  339. // Paints the options and create array of selected id keys
  340. target.startTag("options");
  341. int keyIndex = 0;
  342. // Support for external null selection item id
  343. final Collection<?> ids = getItemIds();
  344. if (isNullSelectionAllowed() && getNullSelectionItemId() != null
  345. && !ids.contains(getNullSelectionItemId())) {
  346. final Object id = getNullSelectionItemId();
  347. // Paints option
  348. target.startTag("so");
  349. paintItem(target, id);
  350. if (isSelected(id)) {
  351. selectedKeys[keyIndex++] = itemIdMapper.key(id);
  352. }
  353. target.endTag("so");
  354. }
  355. final Iterator<?> i = getItemIds().iterator();
  356. // Paints the available selection options from data source
  357. while (i.hasNext()) {
  358. // Gets the option attribute values
  359. final Object id = i.next();
  360. if (!isNullSelectionAllowed() && id != null
  361. && id.equals(getNullSelectionItemId())) {
  362. // Remove item if it's the null selection item but null
  363. // selection is not allowed
  364. continue;
  365. }
  366. final String key = itemIdMapper.key(id);
  367. // add listener for each item, to cause repaint if an item changes
  368. getCaptionChangeListener().addNotifierForItem(id);
  369. target.startTag("so");
  370. paintItem(target, id);
  371. if (isSelected(id) && keyIndex < selectedKeys.length) {
  372. selectedKeys[keyIndex++] = key;
  373. }
  374. target.endTag("so");
  375. }
  376. target.endTag("options");
  377. // ==
  378. // Paint variables
  379. target.addVariable(this, "selected", selectedKeys);
  380. if (isNewItemsAllowed()) {
  381. target.addVariable(this, "newitem", "");
  382. }
  383. }
  384. protected void paintItem(PaintTarget target, Object itemId)
  385. throws PaintException {
  386. final String key = itemIdMapper.key(itemId);
  387. final String caption = getItemCaption(itemId);
  388. final Resource icon = getItemIcon(itemId);
  389. if (icon != null) {
  390. target.addAttribute("icon", icon);
  391. }
  392. target.addAttribute("caption", caption);
  393. if (itemId != null && itemId.equals(getNullSelectionItemId())) {
  394. target.addAttribute("nullselection", true);
  395. }
  396. target.addAttribute("key", key);
  397. if (isSelected(itemId)) {
  398. target.addAttribute("selected", true);
  399. }
  400. }
  401. /**
  402. * Invoked when the value of a variable has changed.
  403. *
  404. * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object,
  405. * java.util.Map)
  406. */
  407. @Override
  408. public void changeVariables(Object source, Map<String, Object> variables) {
  409. // New option entered (and it is allowed)
  410. if (isNewItemsAllowed()) {
  411. final String newitem = (String) variables.get("newitem");
  412. if (newitem != null && newitem.length() > 0) {
  413. getNewItemHandler().addNewItem(newitem);
  414. }
  415. }
  416. // Selection change
  417. if (variables.containsKey("selected")) {
  418. final String[] clientSideSelectedKeys = (String[]) variables
  419. .get("selected");
  420. // Multiselect mode
  421. if (isMultiSelect()) {
  422. // TODO Optimize by adding repaintNotNeeded when applicable
  423. // Converts the key-array to id-set
  424. final LinkedList<Object> acceptedSelections = new LinkedList<Object>();
  425. for (int i = 0; i < clientSideSelectedKeys.length; i++) {
  426. final Object id = itemIdMapper
  427. .get(clientSideSelectedKeys[i]);
  428. if (!isNullSelectionAllowed()
  429. && (id == null || id == getNullSelectionItemId())) {
  430. // skip empty selection if nullselection is not allowed
  431. markAsDirty();
  432. } else if (id != null && containsId(id)) {
  433. acceptedSelections.add(id);
  434. }
  435. }
  436. if (!isNullSelectionAllowed() && acceptedSelections.size() < 1) {
  437. // empty selection not allowed, keep old value
  438. markAsDirty();
  439. return;
  440. }
  441. // Limits the deselection to the set of visible items
  442. // (non-visible items can not be deselected)
  443. Collection<?> visibleNotSelected = getVisibleItemIds();
  444. if (visibleNotSelected != null) {
  445. visibleNotSelected = new HashSet<Object>(visibleNotSelected);
  446. // Don't remove those that will be added to preserve order
  447. visibleNotSelected.removeAll(acceptedSelections);
  448. @SuppressWarnings("unchecked")
  449. Set<Object> newsel = (Set<Object>) getValue();
  450. if (newsel == null) {
  451. newsel = new LinkedHashSet<Object>();
  452. } else {
  453. newsel = new LinkedHashSet<Object>(newsel);
  454. }
  455. newsel.removeAll(visibleNotSelected);
  456. newsel.addAll(acceptedSelections);
  457. setValue(newsel, true);
  458. }
  459. } else {
  460. // Single select mode
  461. if (!isNullSelectionAllowed()
  462. && (clientSideSelectedKeys.length == 0
  463. || clientSideSelectedKeys[0] == null || clientSideSelectedKeys[0] == getNullSelectionItemId())) {
  464. markAsDirty();
  465. return;
  466. }
  467. if (clientSideSelectedKeys.length == 0) {
  468. // Allows deselection only if the deselected item is
  469. // visible
  470. final Object current = getValue();
  471. final Collection<?> visible = getVisibleItemIds();
  472. if (visible != null && visible.contains(current)) {
  473. setValue(null, true);
  474. }
  475. } else {
  476. String clientSelectedKey = clientSideSelectedKeys[0];
  477. if ("null".equals(clientSelectedKey)
  478. || itemIdMapper.containsKey(clientSelectedKey)) {
  479. // Happens to work for nullselection
  480. // (get ("null") -> null))
  481. final Object id = itemIdMapper.get(clientSelectedKey);
  482. if (!isNullSelectionAllowed() && id == null) {
  483. markAsDirty();
  484. } else if (id != null
  485. && id.equals(getNullSelectionItemId())) {
  486. setValue(null, true);
  487. } else {
  488. setValue(id, true);
  489. }
  490. }
  491. }
  492. }
  493. }
  494. }
  495. /**
  496. * TODO refine doc Setter for new item handler that is called when user adds
  497. * new item in newItemAllowed mode.
  498. *
  499. * @param newItemHandler
  500. */
  501. public void setNewItemHandler(NewItemHandler newItemHandler) {
  502. this.newItemHandler = newItemHandler;
  503. }
  504. /**
  505. * TODO refine doc
  506. *
  507. * @return
  508. */
  509. public NewItemHandler getNewItemHandler() {
  510. if (newItemHandler == null) {
  511. newItemHandler = new DefaultNewItemHandler();
  512. }
  513. return newItemHandler;
  514. }
  515. public interface NewItemHandler extends Serializable {
  516. void addNewItem(String newItemCaption);
  517. }
  518. /**
  519. * TODO refine doc
  520. *
  521. * This is a default class that handles adding new items that are typed by
  522. * user to selects container.
  523. *
  524. * By extending this class one may implement some logic on new item addition
  525. * like database inserts.
  526. *
  527. */
  528. public class DefaultNewItemHandler implements NewItemHandler {
  529. @Override
  530. public void addNewItem(String newItemCaption) {
  531. // Checks for readonly
  532. if (isReadOnly()) {
  533. throw new Property.ReadOnlyException();
  534. }
  535. // Adds new option
  536. if (addItem(newItemCaption) != null) {
  537. // Sets the caption property, if used
  538. if (getItemCaptionPropertyId() != null) {
  539. getContainerProperty(newItemCaption,
  540. getItemCaptionPropertyId())
  541. .setValue(newItemCaption);
  542. }
  543. if (isMultiSelect()) {
  544. Set values = new HashSet((Collection) getValue());
  545. values.add(newItemCaption);
  546. setValue(values);
  547. } else {
  548. setValue(newItemCaption);
  549. }
  550. }
  551. }
  552. }
  553. /**
  554. * Gets the visible item ids. In Select, this returns list of all item ids,
  555. * but can be overriden in subclasses if they paint only part of the items
  556. * to the terminal or null if no items is visible.
  557. */
  558. public Collection<?> getVisibleItemIds() {
  559. return getItemIds();
  560. }
  561. /* Property methods */
  562. /**
  563. * Returns the type of the property. <code>getValue</code> and
  564. * <code>setValue</code> methods must be compatible with this type: one can
  565. * safely cast <code>getValue</code> to given type and pass any variable
  566. * assignable to this type as a parameter to <code>setValue</code>.
  567. *
  568. * @return the Type of the property.
  569. */
  570. @Override
  571. public Class<?> getType() {
  572. if (isMultiSelect()) {
  573. return Set.class;
  574. } else {
  575. return Object.class;
  576. }
  577. }
  578. /**
  579. * Gets the selected item id or in multiselect mode a set of selected ids.
  580. *
  581. * @see com.vaadin.ui.AbstractField#getValue()
  582. */
  583. @Override
  584. public Object getValue() {
  585. final Object retValue = super.getValue();
  586. if (isMultiSelect()) {
  587. // If the return value is not a set
  588. if (retValue == null) {
  589. return new HashSet<Object>();
  590. }
  591. if (retValue instanceof Set) {
  592. return Collections.unmodifiableSet((Set<?>) retValue);
  593. } else if (retValue instanceof Collection) {
  594. return new HashSet<Object>((Collection<?>) retValue);
  595. } else {
  596. final Set<Object> s = new HashSet<Object>();
  597. if (items.containsId(retValue)) {
  598. s.add(retValue);
  599. }
  600. return s;
  601. }
  602. } else {
  603. return retValue;
  604. }
  605. }
  606. /**
  607. * Sets the visible value of the property.
  608. *
  609. * <p>
  610. * The value of the select is the selected item id. If the select is in
  611. * multiselect-mode, the value is a set of selected item keys. In
  612. * multiselect mode all collections of id:s can be assigned.
  613. * </p>
  614. *
  615. * @param newValue
  616. * the New selected item or collection of selected items.
  617. * @see com.vaadin.ui.AbstractField#setValue(java.lang.Object)
  618. */
  619. @Override
  620. public void setValue(Object newValue) throws Property.ReadOnlyException {
  621. if (newValue == getNullSelectionItemId()) {
  622. newValue = null;
  623. }
  624. setValue(newValue, false);
  625. }
  626. /**
  627. * Sets the visible value of the property.
  628. *
  629. * <p>
  630. * The value of the select is the selected item id. If the select is in
  631. * multiselect-mode, the value is a set of selected item keys. In
  632. * multiselect mode all collections of id:s can be assigned.
  633. * </p>
  634. *
  635. * @since 7.5.7
  636. * @param newValue
  637. * the New selected item or collection of selected items.
  638. * @param repaintIsNotNeeded
  639. * True if caller is sure that repaint is not needed.
  640. * @param ignoreReadOnly
  641. * True if read-only check should be omitted.
  642. * @see com.vaadin.ui.AbstractField#setValue(java.lang.Object,
  643. * java.lang.Boolean)
  644. */
  645. @Override
  646. protected void setValue(Object newFieldValue, boolean repaintIsNotNeeded,
  647. boolean ignoreReadOnly)
  648. throws com.vaadin.data.Property.ReadOnlyException,
  649. ConversionException, InvalidValueException {
  650. if (isMultiSelect()) {
  651. if (newFieldValue == null) {
  652. super.setValue(new LinkedHashSet<Object>(), repaintIsNotNeeded,
  653. ignoreReadOnly);
  654. } else if (Collection.class.isAssignableFrom(newFieldValue
  655. .getClass())) {
  656. super.setValue(new LinkedHashSet<Object>(
  657. (Collection<?>) newFieldValue), repaintIsNotNeeded,
  658. ignoreReadOnly);
  659. }
  660. } else if (newFieldValue == null || items.containsId(newFieldValue)) {
  661. super.setValue(newFieldValue, repaintIsNotNeeded, ignoreReadOnly);
  662. }
  663. }
  664. /* Container methods */
  665. /**
  666. * Gets the item from the container with given id. If the container does not
  667. * contain the requested item, null is returned.
  668. *
  669. * @param itemId
  670. * the item id.
  671. * @return the item from the container.
  672. */
  673. @Override
  674. public Item getItem(Object itemId) {
  675. return items.getItem(itemId);
  676. }
  677. /**
  678. * Gets the item Id collection from the container.
  679. *
  680. * @return the Collection of item ids.
  681. */
  682. @Override
  683. public Collection<?> getItemIds() {
  684. return items.getItemIds();
  685. }
  686. /**
  687. * Gets the property Id collection from the container.
  688. *
  689. * @return the Collection of property ids.
  690. */
  691. @Override
  692. public Collection<?> getContainerPropertyIds() {
  693. return items.getContainerPropertyIds();
  694. }
  695. /**
  696. * Gets the property type.
  697. *
  698. * @param propertyId
  699. * the Id identifying the property.
  700. * @see com.vaadin.data.Container#getType(java.lang.Object)
  701. */
  702. @Override
  703. public Class<?> getType(Object propertyId) {
  704. return items.getType(propertyId);
  705. }
  706. /*
  707. * Gets the number of items in the container.
  708. *
  709. * @return the Number of items in the container.
  710. *
  711. * @see com.vaadin.data.Container#size()
  712. */
  713. @Override
  714. public int size() {
  715. int size = items.size();
  716. assert size >= 0;
  717. return size;
  718. }
  719. /**
  720. * Tests, if the collection contains an item with given id.
  721. *
  722. * @param itemId
  723. * the Id the of item to be tested.
  724. */
  725. @Override
  726. public boolean containsId(Object itemId) {
  727. if (itemId != null) {
  728. return items.containsId(itemId);
  729. } else {
  730. return false;
  731. }
  732. }
  733. /**
  734. * Gets the Property identified by the given itemId and propertyId from the
  735. * Container
  736. *
  737. * @see com.vaadin.data.Container#getContainerProperty(Object, Object)
  738. */
  739. @Override
  740. public Property getContainerProperty(Object itemId, Object propertyId) {
  741. return items.getContainerProperty(itemId, propertyId);
  742. }
  743. /**
  744. * Adds the new property to all items. Adds a property with given id, type
  745. * and default value to all items in the container.
  746. *
  747. * This functionality is optional. If the function is unsupported, it always
  748. * returns false.
  749. *
  750. * @return True if the operation succeeded.
  751. * @see com.vaadin.data.Container#addContainerProperty(java.lang.Object,
  752. * java.lang.Class, java.lang.Object)
  753. */
  754. @Override
  755. public boolean addContainerProperty(Object propertyId, Class<?> type,
  756. Object defaultValue) throws UnsupportedOperationException {
  757. final boolean retval = items.addContainerProperty(propertyId, type,
  758. defaultValue);
  759. if (retval && !(items instanceof Container.PropertySetChangeNotifier)) {
  760. firePropertySetChange();
  761. }
  762. return retval;
  763. }
  764. /**
  765. * Removes all items from the container.
  766. *
  767. * This functionality is optional. If the function is unsupported, it always
  768. * returns false.
  769. *
  770. * @return True if the operation succeeded.
  771. * @see com.vaadin.data.Container#removeAllItems()
  772. */
  773. @Override
  774. public boolean removeAllItems() throws UnsupportedOperationException {
  775. final boolean retval = items.removeAllItems();
  776. itemIdMapper.removeAll();
  777. if (retval) {
  778. setValue(null);
  779. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  780. fireItemSetChange();
  781. }
  782. }
  783. return retval;
  784. }
  785. /**
  786. * Creates a new item into container with container managed id. The id of
  787. * the created new item is returned. The item can be fetched with getItem()
  788. * method. if the creation fails, null is returned.
  789. *
  790. * @return the Id of the created item or null in case of failure.
  791. * @see com.vaadin.data.Container#addItem()
  792. */
  793. @Override
  794. public Object addItem() throws UnsupportedOperationException {
  795. final Object retval = items.addItem();
  796. if (retval != null
  797. && !(items instanceof Container.ItemSetChangeNotifier)) {
  798. fireItemSetChange();
  799. }
  800. return retval;
  801. }
  802. /**
  803. * Create a new item into container. The created new item is returned and
  804. * ready for setting property values. if the creation fails, null is
  805. * returned. In case the container already contains the item, null is
  806. * returned.
  807. *
  808. * This functionality is optional. If the function is unsupported, it always
  809. * returns null.
  810. *
  811. * @param itemId
  812. * the Identification of the item to be created.
  813. * @return the Created item with the given id, or null in case of failure.
  814. * @see com.vaadin.data.Container#addItem(java.lang.Object)
  815. */
  816. @Override
  817. public Item addItem(Object itemId) throws UnsupportedOperationException {
  818. final Item retval = items.addItem(itemId);
  819. if (retval != null
  820. && !(items instanceof Container.ItemSetChangeNotifier)) {
  821. fireItemSetChange();
  822. }
  823. return retval;
  824. }
  825. /**
  826. * Adds given items with given item ids to container.
  827. *
  828. * @since 7.2
  829. * @param itemId
  830. * item identifiers to be added to underlying container
  831. * @throws UnsupportedOperationException
  832. * if the underlying container don't support adding items with
  833. * identifiers
  834. */
  835. public void addItems(Object... itemId) throws UnsupportedOperationException {
  836. for (Object id : itemId) {
  837. addItem(id);
  838. }
  839. }
  840. /**
  841. * Adds given items with given item ids to container.
  842. *
  843. * @since 7.2
  844. * @param itemIds
  845. * item identifiers to be added to underlying container
  846. * @throws UnsupportedOperationException
  847. * if the underlying container don't support adding items with
  848. * identifiers
  849. */
  850. public void addItems(Collection<?> itemIds)
  851. throws UnsupportedOperationException {
  852. addItems(itemIds.toArray());
  853. }
  854. /*
  855. * (non-Javadoc)
  856. *
  857. * @see com.vaadin.data.Container#removeItem(java.lang.Object)
  858. */
  859. @Override
  860. public boolean removeItem(Object itemId)
  861. throws UnsupportedOperationException {
  862. unselect(itemId);
  863. final boolean retval = items.removeItem(itemId);
  864. itemIdMapper.remove(itemId);
  865. if (retval && !(items instanceof Container.ItemSetChangeNotifier)) {
  866. fireItemSetChange();
  867. }
  868. return retval;
  869. }
  870. /**
  871. * Checks that the current selection is valid, i.e. the selected item ids
  872. * exist in the container. Updates the selection if one or several selected
  873. * item ids are no longer available in the container.
  874. */
  875. @SuppressWarnings("unchecked")
  876. public void sanitizeSelection() {
  877. Object value = getValue();
  878. if (value == null) {
  879. return;
  880. }
  881. boolean changed = false;
  882. if (isMultiSelect()) {
  883. Collection<Object> valueAsCollection = (Collection<Object>) value;
  884. List<Object> newSelection = new ArrayList<Object>(
  885. valueAsCollection.size());
  886. for (Object subValue : valueAsCollection) {
  887. if (containsId(subValue)) {
  888. newSelection.add(subValue);
  889. } else {
  890. changed = true;
  891. }
  892. }
  893. if (changed) {
  894. setValue(newSelection);
  895. }
  896. } else {
  897. if (!containsId(value)) {
  898. setValue(null);
  899. }
  900. }
  901. }
  902. /**
  903. * Removes the property from all items. Removes a property with given id
  904. * from all the items in the container.
  905. *
  906. * This functionality is optional. If the function is unsupported, it always
  907. * returns false.
  908. *
  909. * @return True if the operation succeeded.
  910. * @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object)
  911. */
  912. @Override
  913. public boolean removeContainerProperty(Object propertyId)
  914. throws UnsupportedOperationException {
  915. final boolean retval = items.removeContainerProperty(propertyId);
  916. if (retval && !(items instanceof Container.PropertySetChangeNotifier)) {
  917. firePropertySetChange();
  918. }
  919. return retval;
  920. }
  921. /* Container.Viewer methods */
  922. /**
  923. * Sets the Container that serves as the data source of the viewer.
  924. *
  925. * As a side-effect the fields value (selection) is set to null due old
  926. * selection not necessary exists in new Container.
  927. *
  928. * @see com.vaadin.data.Container.Viewer#setContainerDataSource(Container)
  929. *
  930. * @param newDataSource
  931. * the new data source.
  932. */
  933. @Override
  934. public void setContainerDataSource(Container newDataSource) {
  935. if (newDataSource == null) {
  936. newDataSource = new IndexedContainer();
  937. }
  938. getCaptionChangeListener().clear();
  939. if (items != newDataSource) {
  940. // Removes listeners from the old datasource
  941. if (items != null) {
  942. if (items instanceof Container.ItemSetChangeNotifier) {
  943. ((Container.ItemSetChangeNotifier) items)
  944. .removeItemSetChangeListener(this);
  945. }
  946. if (items instanceof Container.PropertySetChangeNotifier) {
  947. ((Container.PropertySetChangeNotifier) items)
  948. .removePropertySetChangeListener(this);
  949. }
  950. }
  951. // Assigns new data source
  952. items = newDataSource;
  953. // Clears itemIdMapper also
  954. itemIdMapper.removeAll();
  955. // Adds listeners
  956. if (items != null) {
  957. if (items instanceof Container.ItemSetChangeNotifier) {
  958. ((Container.ItemSetChangeNotifier) items)
  959. .addItemSetChangeListener(this);
  960. }
  961. if (items instanceof Container.PropertySetChangeNotifier) {
  962. ((Container.PropertySetChangeNotifier) items)
  963. .addPropertySetChangeListener(this);
  964. }
  965. }
  966. /*
  967. * We expect changing the data source should also clean value. See
  968. * #810, #4607, #5281
  969. */
  970. setValue(null);
  971. markAsDirty();
  972. }
  973. }
  974. /**
  975. * Gets the viewing data-source container.
  976. *
  977. * @see com.vaadin.data.Container.Viewer#getContainerDataSource()
  978. */
  979. @Override
  980. public Container getContainerDataSource() {
  981. return items;
  982. }
  983. /* Select attributes */
  984. /**
  985. * Is the select in multiselect mode? In multiselect mode
  986. *
  987. * @return the Value of property multiSelect.
  988. */
  989. public boolean isMultiSelect() {
  990. return multiSelect;
  991. }
  992. /**
  993. * Sets the multiselect mode. Setting multiselect mode false may lose
  994. * selection information: if selected items set contains one or more
  995. * selected items, only one of the selected items is kept as selected.
  996. *
  997. * Subclasses of AbstractSelect can choose not to support changing the
  998. * multiselect mode, and may throw {@link UnsupportedOperationException}.
  999. *
  1000. * @param multiSelect
  1001. * the New value of property multiSelect.
  1002. */
  1003. public void setMultiSelect(boolean multiSelect) {
  1004. if (multiSelect && getNullSelectionItemId() != null) {
  1005. throw new IllegalStateException(
  1006. "Multiselect and NullSelectionItemId can not be set at the same time.");
  1007. }
  1008. if (multiSelect != this.multiSelect) {
  1009. // Selection before mode change
  1010. final Object oldValue = getValue();
  1011. this.multiSelect = multiSelect;
  1012. // Convert the value type
  1013. if (multiSelect) {
  1014. final Set<Object> s = new HashSet<Object>();
  1015. if (oldValue != null) {
  1016. s.add(oldValue);
  1017. }
  1018. setValue(s);
  1019. } else {
  1020. final Set<?> s = (Set<?>) oldValue;
  1021. if (s == null || s.isEmpty()) {
  1022. setValue(null);
  1023. } else {
  1024. // Set the single select to contain only the first
  1025. // selected value in the multiselect
  1026. setValue(s.iterator().next());
  1027. }
  1028. }
  1029. markAsDirty();
  1030. }
  1031. }
  1032. /**
  1033. * Does the select allow adding new options by the user. If true, the new
  1034. * options can be added to the Container. The text entered by the user is
  1035. * used as id. Note that data-source must allow adding new items.
  1036. *
  1037. * @return True if additions are allowed.
  1038. */
  1039. public boolean isNewItemsAllowed() {
  1040. return allowNewOptions;
  1041. }
  1042. /**
  1043. * Enables or disables possibility to add new options by the user.
  1044. *
  1045. * @param allowNewOptions
  1046. * the New value of property allowNewOptions.
  1047. */
  1048. public void setNewItemsAllowed(boolean allowNewOptions) {
  1049. // Only handle change requests
  1050. if (this.allowNewOptions != allowNewOptions) {
  1051. this.allowNewOptions = allowNewOptions;
  1052. markAsDirty();
  1053. }
  1054. }
  1055. /**
  1056. * Override the caption of an item. Setting caption explicitly overrides id,
  1057. * item and index captions.
  1058. *
  1059. * @param itemId
  1060. * the id of the item to be recaptioned.
  1061. * @param caption
  1062. * the New caption.
  1063. */
  1064. public void setItemCaption(Object itemId, String caption) {
  1065. if (itemId != null) {
  1066. itemCaptions.put(itemId, caption);
  1067. markAsDirty();
  1068. }
  1069. }
  1070. /**
  1071. * Gets the caption of an item. The caption is generated as specified by the
  1072. * item caption mode. See <code>setItemCaptionMode()</code> for more
  1073. * details.
  1074. *
  1075. * @param itemId
  1076. * the id of the item to be queried.
  1077. * @return the caption for specified item.
  1078. */
  1079. public String getItemCaption(Object itemId) {
  1080. // Null items can not be found
  1081. if (itemId == null) {
  1082. return null;
  1083. }
  1084. String caption = null;
  1085. switch (getItemCaptionMode()) {
  1086. case ID:
  1087. caption = idToCaption(itemId);
  1088. break;
  1089. case ID_TOSTRING:
  1090. caption = itemId.toString();
  1091. break;
  1092. case INDEX:
  1093. if (items instanceof Container.Indexed) {
  1094. caption = String.valueOf(((Container.Indexed) items)
  1095. .indexOfId(itemId));
  1096. } else {
  1097. caption = "ERROR: Container is not indexed";
  1098. }
  1099. break;
  1100. case ITEM:
  1101. final Item i = getItem(itemId);
  1102. if (i != null) {
  1103. caption = i.toString();
  1104. }
  1105. break;
  1106. case EXPLICIT:
  1107. caption = itemCaptions.get(itemId);
  1108. break;
  1109. case EXPLICIT_DEFAULTS_ID:
  1110. caption = itemCaptions.get(itemId);
  1111. if (caption == null) {
  1112. caption = idToCaption(itemId);
  1113. }
  1114. break;
  1115. case PROPERTY:
  1116. final Property<?> p = getContainerProperty(itemId,
  1117. getItemCaptionPropertyId());
  1118. if (p != null) {
  1119. Object value = p.getValue();
  1120. if (value != null) {
  1121. caption = value.toString();
  1122. }
  1123. }
  1124. break;
  1125. }
  1126. // All items must have some captions
  1127. return caption != null ? caption : "";
  1128. }
  1129. private String idToCaption(Object itemId) {
  1130. try {
  1131. Converter<String, Object> c = (Converter<String, Object>) ConverterUtil
  1132. .getConverter(String.class, itemId.getClass(), getSession());
  1133. return ConverterUtil.convertFromModel(itemId, String.class, c,
  1134. getLocale());
  1135. } catch (Exception e) {
  1136. return itemId.toString();
  1137. }
  1138. }
  1139. /**
  1140. * Sets the icon for an item.
  1141. *
  1142. * @param itemId
  1143. * the id of the item to be assigned an icon.
  1144. * @param icon
  1145. * the icon to use or null.
  1146. */
  1147. public void setItemIcon(Object itemId, Resource icon) {
  1148. if (itemId != null) {
  1149. if (icon == null) {
  1150. itemIcons.remove(itemId);
  1151. } else {
  1152. itemIcons.put(itemId, icon);
  1153. }
  1154. markAsDirty();
  1155. }
  1156. }
  1157. /**
  1158. * Gets the item icon.
  1159. *
  1160. * @param itemId
  1161. * the id of the item to be assigned an icon.
  1162. * @return the icon for the item or null, if not specified.
  1163. */
  1164. public Resource getItemIcon(Object itemId) {
  1165. final Resource explicit = itemIcons.get(itemId);
  1166. if (explicit != null) {
  1167. return explicit;
  1168. }
  1169. if (getItemIconPropertyId() == null) {
  1170. return null;
  1171. }
  1172. final Property<?> ip = getContainerProperty(itemId,
  1173. getItemIconPropertyId());
  1174. if (ip == null) {
  1175. return null;
  1176. }
  1177. final Object icon = ip.getValue();
  1178. if (icon instanceof Resource) {
  1179. return (Resource) icon;
  1180. }
  1181. return null;
  1182. }
  1183. /**
  1184. * Sets the item caption mode.
  1185. *
  1186. * See {@link ItemCaptionMode} for a description of the modes.
  1187. * <p>
  1188. * {@link ItemCaptionMode#EXPLICIT_DEFAULTS_ID} is the default mode.
  1189. * </p>
  1190. *
  1191. * @param mode
  1192. * the One of the modes listed above.
  1193. */
  1194. public void setItemCaptionMode(ItemCaptionMode mode) {
  1195. if (mode != null) {
  1196. itemCaptionMode = mode;
  1197. markAsDirty();
  1198. }
  1199. }
  1200. /**
  1201. * Gets the item caption mode.
  1202. *
  1203. * <p>
  1204. * The mode can be one of the following ones:
  1205. * <ul>
  1206. * <li><code>ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID</code> : Items
  1207. * Id-objects <code>toString</code> is used as item caption. If caption is
  1208. * explicitly specified, it overrides the id-caption.
  1209. * <li><code>ITEM_CAPTION_MODE_ID</code> : Items Id-objects
  1210. * <code>toString</code> is used as item caption.</li>
  1211. * <li><code>ITEM_CAPTION_MODE_ITEM</code> : Item-objects
  1212. * <code>toString</code> is used as item caption.</li>
  1213. * <li><code>ITEM_CAPTION_MODE_INDEX</code> : The index of the item is used
  1214. * as item caption. The index mode can only be used with the containers
  1215. * implementing <code>Container.Indexed</code> interface.</li>
  1216. * <li><code>ITEM_CAPTION_MODE_EXPLICIT</code> : The item captions must be
  1217. * explicitly specified.</li>
  1218. * <li><code>ITEM_CAPTION_MODE_PROPERTY</code> : The item captions are read
  1219. * from property, that must be specified with
  1220. * <code>setItemCaptionPropertyId</code>.</li>
  1221. * </ul>
  1222. * The <code>ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID</code> is the default
  1223. * mode.
  1224. * </p>
  1225. *
  1226. * @return the One of the modes listed above.
  1227. */
  1228. public ItemCaptionMode getItemCaptionMode() {
  1229. return itemCaptionMode;
  1230. }
  1231. /**
  1232. * Sets the item caption property.
  1233. *
  1234. * <p>
  1235. * Setting the id to a existing property implicitly sets the item caption
  1236. * mode to <code>ITEM_CAPTION_MODE_PROPERTY</code>. If the object is in
  1237. * <code>ITEM_CAPTION_MODE_PROPERTY</code> mode, setting caption property id
  1238. * null resets the item caption mode to
  1239. * <code>ITEM_CAPTION_EXPLICIT_DEFAULTS_ID</code>.
  1240. * </p>
  1241. * <p>
  1242. * Note that the type of the property used for caption must be String
  1243. * </p>
  1244. * <p>
  1245. * Setting the property id to null disables this feature. The id is null by
  1246. * default
  1247. * </p>
  1248. * .
  1249. *
  1250. * @param propertyId
  1251. * the id of the property.
  1252. *
  1253. */
  1254. public void setItemCaptionPropertyId(Object propertyId) {
  1255. if (propertyId != null) {
  1256. itemCaptionPropertyId = propertyId;
  1257. setItemCaptionMode(ITEM_CAPTION_MODE_PROPERTY);
  1258. markAsDirty();
  1259. } else {
  1260. itemCaptionPropertyId = null;
  1261. if (getItemCaptionMode() == ITEM_CAPTION_MODE_PROPERTY) {
  1262. setItemCaptionMode(ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID);
  1263. }
  1264. markAsDirty();
  1265. }
  1266. }
  1267. /**
  1268. * Gets the item caption property.
  1269. *
  1270. * @return the Id of the property used as item caption source.
  1271. */
  1272. public Object getItemCaptionPropertyId() {
  1273. return itemCaptionPropertyId;
  1274. }
  1275. /**
  1276. * Sets the item icon property.
  1277. *
  1278. * <p>
  1279. * If the property id is set to a valid value, each item is given an icon
  1280. * got from the given property of the items. The type of the property must
  1281. * be assignable to Resource.
  1282. * </p>
  1283. *
  1284. * <p>
  1285. * Note : The icons set with <code>setItemIcon</code> function override the
  1286. * icons from the property.
  1287. * </p>
  1288. *
  1289. * <p>
  1290. * Setting the property id to null disables this feature. The id is null by
  1291. * default
  1292. * </p>
  1293. * .
  1294. *
  1295. * @param propertyId
  1296. * the id of the property that specifies icons for items or null
  1297. * @throws IllegalArgumentException
  1298. * If the propertyId is not in the container or is not of a
  1299. * valid type
  1300. */
  1301. public void setItemIconPropertyId(Object propertyId)
  1302. throws IllegalArgumentException {
  1303. if (propertyId == null) {
  1304. itemIconPropertyId = null;
  1305. } else if (!getContainerPropertyIds().contains(propertyId)) {
  1306. throw new IllegalArgumentException(
  1307. "Property id not found in the container");
  1308. } else if (Resource.class.isAssignableFrom(getType(propertyId))) {
  1309. itemIconPropertyId = propertyId;
  1310. } else {
  1311. throw new IllegalArgumentException(
  1312. "Property type must be assignable to Resource");
  1313. }
  1314. markAsDirty();
  1315. }
  1316. /**
  1317. * Gets the item icon property.
  1318. *
  1319. * <p>
  1320. * If the property id is set to a valid value, each item is given an icon
  1321. * got from the given property of the items. The type of the property must
  1322. * be assignable to Icon.
  1323. * </p>
  1324. *
  1325. * <p>
  1326. * Note : The icons set with <code>setItemIcon</code> function override the
  1327. * icons from the property.
  1328. * </p>
  1329. *
  1330. * <p>
  1331. * Setting the property id to null disables this feature. The id is null by
  1332. * default
  1333. * </p>
  1334. * .
  1335. *
  1336. * @return the Id of the property containing the item icons.
  1337. */
  1338. public Object getItemIconPropertyId() {
  1339. return itemIconPropertyId;
  1340. }
  1341. /**
  1342. * Tests if an item is selected.
  1343. *
  1344. * <p>
  1345. * In single select mode testing selection status of the item identified by
  1346. * {@link #getNullSelectionItemId()} returns true if the value of the
  1347. * property is null.
  1348. * </p>
  1349. *
  1350. * @param itemId
  1351. * the Id the of the item to be tested.
  1352. * @see #getNullSelectionItemId()
  1353. * @see #setNullSelectionItemId(Object)
  1354. *
  1355. */
  1356. public boolean isSelected(Object itemId) {
  1357. if (itemId == null) {
  1358. return false;
  1359. }
  1360. if (isMultiSelect()) {
  1361. return ((Set<?>) getValue()).contains(itemId);
  1362. } else {
  1363. final Object value = getValue();
  1364. return itemId.equals(value == null ? getNullSelectionItemId()
  1365. : value);
  1366. }
  1367. }
  1368. /**
  1369. * Selects an item.
  1370. *
  1371. * <p>
  1372. * In single select mode selecting item identified by
  1373. * {@link #getNullSelectionItemId()} sets the value of the property to null.
  1374. * </p>
  1375. *
  1376. * @param itemId
  1377. * the identifier of Item to be selected.
  1378. * @see #getNullSelectionItemId()
  1379. * @see #setNullSelectionItemId(Object)
  1380. *
  1381. */
  1382. public void select(Object itemId) {
  1383. if (!isMultiSelect()) {
  1384. setValue(itemId);
  1385. } else if (!isSelected(itemId) && itemId != null
  1386. && items.containsId(itemId)) {
  1387. final Set<Object> s = new HashSet<Object>((Set<?>) getValue());
  1388. s.add(itemId);
  1389. setValue(s);
  1390. }
  1391. }
  1392. /**
  1393. * Unselects an item.
  1394. *
  1395. * @param itemId
  1396. * the identifier of the Item to be unselected.
  1397. * @see #getNullSelectionItemId()
  1398. * @see #setNullSelectionItemId(Object)
  1399. *
  1400. */
  1401. public void unselect(Object itemId) {
  1402. if (isSelected(itemId)) {
  1403. if (isMultiSelect()) {
  1404. final Set<Object> s = new HashSet<Object>((Set<?>) getValue());
  1405. s.remove(itemId);
  1406. setValue(s);
  1407. } else {
  1408. setValue(null);
  1409. }
  1410. }
  1411. }
  1412. /**
  1413. * Notifies this listener that the Containers contents has changed.
  1414. *
  1415. * @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent)
  1416. */
  1417. @Override
  1418. public void containerPropertySetChange(
  1419. Container.PropertySetChangeEvent event) {
  1420. firePropertySetChange();
  1421. }
  1422. /**
  1423. * Adds a new Property set change listener for this Container.
  1424. *
  1425. * @see com.vaadin.data.Container.PropertySetChangeNotifier#addListener(com.vaadin.data.Container.PropertySetChangeListener)
  1426. */
  1427. @Override
  1428. public void addPropertySetChangeListener(
  1429. Container.PropertySetChangeListener listener) {
  1430. if (propertySetEventListeners == null) {
  1431. propertySetEventListeners = new LinkedHashSet<Container.PropertySetChangeListener>();
  1432. }
  1433. propertySetEventListeners.add(listener);
  1434. }
  1435. /**
  1436. * @deprecated As of 7.0, replaced by
  1437. * {@link #addPropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)}
  1438. **/
  1439. @Override
  1440. @Deprecated
  1441. public void addListener(Container.PropertySetChangeListener listener) {
  1442. addPropertySetChangeListener(listener);
  1443. }
  1444. /**
  1445. * Removes a previously registered Property set change listener.
  1446. *
  1447. * @see com.vaadin.data.Container.PropertySetChangeNotifier#removeListener(com.vaadin.data.Container.PropertySetChangeListener)
  1448. */
  1449. @Override
  1450. public void removePropertySetChangeListener(
  1451. Container.PropertySetChangeListener listener) {
  1452. if (propertySetEventListeners != null) {
  1453. propertySetEventListeners.remove(listener);
  1454. if (propertySetEventListeners.isEmpty()) {
  1455. propertySetEventListeners = null;
  1456. }
  1457. }
  1458. }
  1459. /**
  1460. * @deprecated As of 7.0, replaced by
  1461. * {@link #removePropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)}
  1462. **/
  1463. @Override
  1464. @Deprecated
  1465. public void removeListener(Container.PropertySetChangeListener listener) {
  1466. removePropertySetChangeListener(listener);
  1467. }
  1468. /**
  1469. * Adds an Item set change listener for the object.
  1470. *
  1471. * @see com.vaadin.data.Container.ItemSetChangeNotifier#addListener(com.vaadin.data.Container.ItemSetChangeListener)
  1472. */
  1473. @Override
  1474. public void addItemSetChangeListener(
  1475. Container.ItemSetChangeListener listener) {
  1476. if (itemSetEventListeners == null) {
  1477. itemSetEventListeners = new LinkedHashSet<Container.ItemSetChangeListener>();
  1478. }
  1479. itemSetEventListeners.add(listener);
  1480. }
  1481. /**
  1482. * @deprecated As of 7.0, replaced by
  1483. * {@link #addItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)}
  1484. **/
  1485. @Override
  1486. @Deprecated
  1487. public void addListener(Container.ItemSetChangeListener listener) {
  1488. addItemSetChangeListener(listener);
  1489. }
  1490. /**
  1491. * Removes the Item set change listener from the object.
  1492. *
  1493. * @see com.vaadin.data.Container.ItemSetChangeNotifier#removeListener(com.vaadin.data.Container.ItemSetChangeListener)
  1494. */
  1495. @Override
  1496. public void removeItemSetChangeListener(
  1497. Container.ItemSetChangeListener listener) {
  1498. if (itemSetEventListeners != null) {
  1499. itemSetEventListeners.remove(listener);
  1500. if (itemSetEventListeners.isEmpty()) {
  1501. itemSetEventListeners = null;
  1502. }
  1503. }
  1504. }
  1505. /**
  1506. * @deprecated As of 7.0, replaced by
  1507. * {@link #removeItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)}
  1508. **/
  1509. @Override
  1510. @Deprecated
  1511. public void removeListener(Container.ItemSetChangeListener listener) {
  1512. removeItemSetChangeListener(listener);
  1513. }
  1514. @Override
  1515. public Collection<?> getListeners(Class<?> eventType) {
  1516. if (Container.ItemSetChangeEvent.class.isAssignableFrom(eventType)) {
  1517. if (itemSetEventListeners == null) {
  1518. return Collections.EMPTY_LIST;
  1519. } else {
  1520. return Collections
  1521. .unmodifiableCollection(itemSetEventListeners);
  1522. }
  1523. } else if (Container.PropertySetChangeEvent.class
  1524. .isAssignableFrom(eventType)) {
  1525. if (propertySetEventListeners == null) {
  1526. return Collections.EMPTY_LIST;
  1527. } else {
  1528. return Collections
  1529. .unmodifiableCollection(propertySetEventListeners);
  1530. }
  1531. }
  1532. return super.getListeners(eventType);
  1533. }
  1534. /**
  1535. * Lets the listener know a Containers Item set has changed.
  1536. *
  1537. * @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent)
  1538. */
  1539. @Override
  1540. public void containerItemSetChange(Container.ItemSetChangeEvent event) {
  1541. // Clears the item id mapping table
  1542. itemIdMapper.removeAll();
  1543. // Notify all listeners
  1544. fireItemSetChange();
  1545. }
  1546. /**
  1547. * Fires the property set change event.
  1548. */
  1549. protected void firePropertySetChange() {
  1550. if (propertySetEventListeners != null
  1551. && !propertySetEventListeners.isEmpty()) {
  1552. final Container.PropertySetChangeEvent event = new PropertySetChangeEvent(
  1553. this);
  1554. final Object[] listeners = propertySetEventListeners.toArray();
  1555. for (int i = 0; i < listeners.length; i++) {
  1556. ((Container.PropertySetChangeListener) listeners[i])
  1557. .containerPropertySetChange(event);
  1558. }
  1559. }
  1560. markAsDirty();
  1561. }
  1562. /**
  1563. * Fires the item set change event.
  1564. */
  1565. protected void fireItemSetChange() {
  1566. if (itemSetEventListeners != null && !itemSetEventListeners.isEmpty()) {
  1567. final Container.ItemSetChangeEvent event = new ItemSetChangeEvent(
  1568. this);
  1569. final Object[] listeners = itemSetEventListeners.toArray();
  1570. for (int i = 0; i < listeners.length; i++) {
  1571. ((Container.ItemSetChangeListener) listeners[i])
  1572. .containerItemSetChange(event);
  1573. }
  1574. }
  1575. markAsDirty();
  1576. }
  1577. /**
  1578. * Implementation of item set change event.
  1579. */
  1580. private static class ItemSetChangeEvent extends EventObject implements
  1581. Serializable, Container.ItemSetChangeEvent {
  1582. private ItemSetChangeEvent(Container source) {
  1583. super(source);
  1584. }
  1585. /**
  1586. * Gets the Property where the event occurred.
  1587. *
  1588. * @see com.vaadin.data.Container.ItemSetChangeEvent#getContainer()
  1589. */
  1590. @Override
  1591. public Container getContainer() {
  1592. return (Container) getSource();
  1593. }
  1594. }
  1595. /**
  1596. * Implementation of property set change event.
  1597. */
  1598. private static class PropertySetChangeEvent extends EventObject implements
  1599. Container.PropertySetChangeEvent, Serializable {
  1600. private PropertySetChangeEvent(Container source) {
  1601. super(source);
  1602. }
  1603. /**
  1604. * Retrieves the Container whose contents have been modified.
  1605. *
  1606. * @see com.vaadin.data.Container.PropertySetChangeEvent#getContainer()
  1607. */
  1608. @Override
  1609. public Container getContainer() {
  1610. return (Container) getSource();
  1611. }
  1612. }
  1613. /**
  1614. * For multi-selectable fields, also an empty collection of values is
  1615. * considered to be an empty field.
  1616. *
  1617. * @see AbstractField#isEmpty().
  1618. */
  1619. @Override
  1620. public boolean isEmpty() {
  1621. if (!multiSelect) {
  1622. return super.isEmpty();
  1623. } else {
  1624. Object value = getValue();
  1625. return super.isEmpty()
  1626. || (value instanceof Collection && ((Collection<?>) value)
  1627. .isEmpty());
  1628. }
  1629. }
  1630. /**
  1631. * Allow or disallow empty selection by the user. If the select is in
  1632. * single-select mode, you can make an item represent the empty selection by
  1633. * calling <code>setNullSelectionItemId()</code>. This way you can for
  1634. * instance set an icon and caption for the null selection item.
  1635. *
  1636. * @param nullSelectionAllowed
  1637. * whether or not to allow empty selection
  1638. * @see #setNullSelectionItemId(Object)
  1639. * @see #isNullSelectionAllowed()
  1640. */
  1641. public void setNullSelectionAllowed(boolean nullSelectionAllowed) {
  1642. if (nullSelectionAllowed != this.nullSelectionAllowed) {
  1643. this.nullSelectionAllowed = nullSelectionAllowed;
  1644. markAsDirty();
  1645. }
  1646. }
  1647. /**
  1648. * Checks if null empty selection is allowed by the user.
  1649. *
  1650. * @return whether or not empty selection is allowed
  1651. * @see #setNullSelectionAllowed(boolean)
  1652. */
  1653. public boolean isNullSelectionAllowed() {
  1654. return nullSelectionAllowed;
  1655. }
  1656. /**
  1657. * Returns the item id that represents null value of this select in single
  1658. * select mode.
  1659. *
  1660. * <p>
  1661. * Data interface does not support nulls as item ids. Selecting the item
  1662. * identified by this id is the same as selecting no items at all. This
  1663. * setting only affects the single select mode.
  1664. * </p>
  1665. *
  1666. * @return the Object Null value item id.
  1667. * @see #setNullSelectionItemId(Object)
  1668. * @see #isSelected(Object)
  1669. * @see #select(Object)
  1670. */
  1671. public Object getNullSelectionItemId() {
  1672. return nullSelectionItemId;
  1673. }
  1674. /**
  1675. * Sets the item id that represents null value of this select.
  1676. *
  1677. * <p>
  1678. * Data interface does not support nulls as item ids. Selecting the item
  1679. * identified by this id is the same as selecting no items at all. This
  1680. * setting only affects the single select mode.
  1681. * </p>
  1682. *
  1683. * @param nullSelectionItemId
  1684. * the nullSelectionItemId to set.
  1685. * @see #getNullSelectionItemId()
  1686. * @see #isSelected(Object)
  1687. * @see #select(Object)
  1688. */
  1689. public void setNullSelectionItemId(Object nullSelectionItemId) {
  1690. if (nullSelectionItemId != null && isMultiSelect()) {
  1691. throw new IllegalStateException(
  1692. "Multiselect and NullSelectionItemId can not be set at the same time.");
  1693. }
  1694. this.nullSelectionItemId = nullSelectionItemId;
  1695. }
  1696. /**
  1697. * Notifies the component that it is connected to an application.
  1698. *
  1699. * @see com.vaadin.ui.AbstractField#attach()
  1700. */
  1701. @Override
  1702. public void attach() {
  1703. super.attach();
  1704. }
  1705. /**
  1706. * Detaches the component from application.
  1707. *
  1708. * @see com.vaadin.ui.AbstractComponent#detach()
  1709. */
  1710. @Override
  1711. public void detach() {
  1712. getCaptionChangeListener().clear();
  1713. super.detach();
  1714. }
  1715. // Caption change listener
  1716. protected CaptionChangeListener getCaptionChangeListener() {
  1717. if (captionChangeListener == null) {
  1718. captionChangeListener = new CaptionChangeListener();
  1719. }
  1720. return captionChangeListener;
  1721. }
  1722. /**
  1723. * This is a listener helper for Item and Property changes that should cause
  1724. * a repaint. It should be attached to all items that are displayed, and the
  1725. * default implementation does this in paintContent(). Especially
  1726. * "lazyloading" components should take care to add and remove listeners as
  1727. * appropriate. Call addNotifierForItem() for each painted item (and
  1728. * remember to clear).
  1729. *
  1730. * NOTE: singleton, use getCaptionChangeListener().
  1731. *
  1732. */
  1733. protected class CaptionChangeListener implements
  1734. Item.PropertySetChangeListener, Property.ValueChangeListener {
  1735. // TODO clean this up - type is either Item.PropertySetChangeNotifier or
  1736. // Property.ValueChangeNotifier
  1737. HashSet<Object> captionChangeNotifiers = new HashSet<Object>();
  1738. public void addNotifierForItem(Object itemId) {
  1739. switch (getItemCaptionMode()) {
  1740. case ITEM:
  1741. final Item i = getItem(itemId);
  1742. if (i == null) {
  1743. return;
  1744. }
  1745. if (i instanceof Item.PropertySetChangeNotifier) {
  1746. ((Item.PropertySetChangeNotifier) i)
  1747. .addPropertySetChangeListener(getCaptionChangeListener());
  1748. captionChangeNotifiers.add(i);
  1749. }
  1750. Collection<?> pids = i.getItemPropertyIds();
  1751. if (pids != null) {
  1752. for (Iterator<?> it = pids.iterator(); it.hasNext();) {
  1753. Property<?> p = i.getItemProperty(it.next());
  1754. if (p != null
  1755. && p instanceof Property.ValueChangeNotifier) {
  1756. ((Property.ValueChangeNotifier) p)
  1757. .addValueChangeListener(getCaptionChangeListener());
  1758. captionChangeNotifiers.add(p);
  1759. }
  1760. }
  1761. }
  1762. break;
  1763. case PROPERTY:
  1764. final Property<?> p = getContainerProperty(itemId,
  1765. getItemCaptionPropertyId());
  1766. if (p != null && p instanceof Property.ValueChangeNotifier) {
  1767. ((Property.ValueChangeNotifier) p)
  1768. .addValueChangeListener(getCaptionChangeListener());
  1769. captionChangeNotifiers.add(p);
  1770. }
  1771. break;
  1772. }
  1773. if (getItemIconPropertyId() != null) {
  1774. final Property p = getContainerProperty(itemId,
  1775. getItemIconPropertyId());
  1776. if (p != null && p instanceof Property.ValueChangeNotifier) {
  1777. ((Property.ValueChangeNotifier) p)
  1778. .addValueChangeListener(getCaptionChangeListener());
  1779. captionChangeNotifiers.add(p);
  1780. }
  1781. }
  1782. }
  1783. public void clear() {
  1784. for (Iterator<Object> it = captionChangeNotifiers.iterator(); it
  1785. .hasNext();) {
  1786. Object notifier = it.next();
  1787. if (notifier instanceof Item.PropertySetChangeNotifier) {
  1788. ((Item.PropertySetChangeNotifier) notifier)
  1789. .removePropertySetChangeListener(getCaptionChangeListener());
  1790. } else {
  1791. ((Property.ValueChangeNotifier) notifier)
  1792. .removeValueChangeListener(getCaptionChangeListener());
  1793. }
  1794. }
  1795. captionChangeNotifiers.clear();
  1796. }
  1797. @Override
  1798. public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
  1799. markAsDirty();
  1800. }
  1801. @Override
  1802. public void itemPropertySetChange(
  1803. com.vaadin.data.Item.PropertySetChangeEvent event) {
  1804. markAsDirty();
  1805. }
  1806. }
  1807. /**
  1808. * Criterion which accepts a drop only if the drop target is (one of) the
  1809. * given Item identifier(s). Criterion can be used only on a drop targets
  1810. * that extends AbstractSelect like {@link Table} and {@link Tree}. The
  1811. * target and identifiers of valid Items are given in constructor.
  1812. *
  1813. * @since 6.3
  1814. */
  1815. public static class TargetItemIs extends AbstractItemSetCriterion {
  1816. /**
  1817. * @param select
  1818. * the select implementation that is used as a drop target
  1819. * @param itemId
  1820. * the identifier(s) that are valid drop locations
  1821. */
  1822. public TargetItemIs(AbstractSelect select, Object... itemId) {
  1823. super(select, itemId);
  1824. }
  1825. @Override
  1826. public boolean accept(DragAndDropEvent dragEvent) {
  1827. AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent
  1828. .getTargetDetails();
  1829. if (dropTargetData.getTarget() != select) {
  1830. return false;
  1831. }
  1832. return itemIds.contains(dropTargetData.getItemIdOver());
  1833. }
  1834. }
  1835. /**
  1836. * Abstract helper class to implement item id based criterion.
  1837. *
  1838. * Note, inner class used not to open itemIdMapper for public access.
  1839. *
  1840. * @since 6.3
  1841. *
  1842. */
  1843. private static abstract class AbstractItemSetCriterion extends
  1844. ClientSideCriterion {
  1845. protected final Collection<Object> itemIds = new HashSet<Object>();
  1846. protected AbstractSelect select;
  1847. public AbstractItemSetCriterion(AbstractSelect select, Object... itemId) {
  1848. if (itemIds == null || select == null) {
  1849. throw new IllegalArgumentException(
  1850. "Accepted item identifiers must be accepted.");
  1851. }
  1852. Collections.addAll(itemIds, itemId);
  1853. this.select = select;
  1854. }
  1855. @Override
  1856. public void paintContent(PaintTarget target) throws PaintException {
  1857. super.paintContent(target);
  1858. String[] keys = new String[itemIds.size()];
  1859. int i = 0;
  1860. for (Object itemId : itemIds) {
  1861. String key = select.itemIdMapper.key(itemId);
  1862. keys[i++] = key;
  1863. }
  1864. target.addAttribute("keys", keys);
  1865. target.addAttribute("s", select);
  1866. }
  1867. }
  1868. /**
  1869. * This criterion accepts a only a {@link Transferable} that contains given
  1870. * Item (practically its identifier) from a specific AbstractSelect.
  1871. *
  1872. * @since 6.3
  1873. */
  1874. public static class AcceptItem extends AbstractItemSetCriterion {
  1875. /**
  1876. * @param select
  1877. * the select from which the item id's are checked
  1878. * @param itemId
  1879. * the item identifier(s) of the select that are accepted
  1880. */
  1881. public AcceptItem(AbstractSelect select, Object... itemId) {
  1882. super(select, itemId);
  1883. }
  1884. @Override
  1885. public boolean accept(DragAndDropEvent dragEvent) {
  1886. DataBoundTransferable transferable = (DataBoundTransferable) dragEvent
  1887. .getTransferable();
  1888. if (transferable.getSourceComponent() != select) {
  1889. return false;
  1890. }
  1891. return itemIds.contains(transferable.getItemId());
  1892. }
  1893. /**
  1894. * A simple accept criterion which ensures that {@link Transferable}
  1895. * contains an {@link Item} (or actually its identifier). In other words
  1896. * the criterion check that drag is coming from a {@link Container} like
  1897. * {@link Tree} or {@link Table}.
  1898. */
  1899. public static final ClientSideCriterion ALL = new ContainsDataFlavor(
  1900. "itemId");
  1901. }
  1902. /**
  1903. * TargetDetails implementation for subclasses of {@link AbstractSelect}
  1904. * that implement {@link DropTarget}.
  1905. *
  1906. * @since 6.3
  1907. */
  1908. public class AbstractSelectTargetDetails extends TargetDetailsImpl {
  1909. /**
  1910. * The item id over which the drag event happened.
  1911. */
  1912. protected Object idOver;
  1913. /**
  1914. * Constructor that automatically converts itemIdOver key to
  1915. * corresponding item Id
  1916. *
  1917. */
  1918. protected AbstractSelectTargetDetails(Map<String, Object> rawVariables) {
  1919. super(rawVariables, (DropTarget) AbstractSelect.this);
  1920. // eagar fetch itemid, mapper may be emptied
  1921. String keyover = (String) getData("itemIdOver");
  1922. if (keyover != null) {
  1923. idOver = itemIdMapper.get(keyover);
  1924. }
  1925. }
  1926. /**
  1927. * If the drag operation is currently over an {@link Item}, this method
  1928. * returns the identifier of that {@link Item}.
  1929. *
  1930. */
  1931. public Object getItemIdOver() {
  1932. return idOver;
  1933. }
  1934. /**
  1935. * Returns a detailed vertical location where the drop happened on Item.
  1936. */
  1937. public VerticalDropLocation getDropLocation() {
  1938. String detail = (String) getData("detail");
  1939. if (detail == null) {
  1940. return null;
  1941. }
  1942. return VerticalDropLocation.valueOf(detail);
  1943. }
  1944. }
  1945. /**
  1946. * An accept criterion to accept drops only on a specific vertical location
  1947. * of an item.
  1948. * <p>
  1949. * This accept criterion is currently usable in Tree and Table
  1950. * implementations.
  1951. */
  1952. public static class VerticalLocationIs extends TargetDetailIs {
  1953. public static VerticalLocationIs TOP = new VerticalLocationIs(
  1954. VerticalDropLocation.TOP);
  1955. public static VerticalLocationIs BOTTOM = new VerticalLocationIs(
  1956. VerticalDropLocation.BOTTOM);
  1957. public static VerticalLocationIs MIDDLE = new VerticalLocationIs(
  1958. VerticalDropLocation.MIDDLE);
  1959. private VerticalLocationIs(VerticalDropLocation l) {
  1960. super("detail", l.name());
  1961. }
  1962. }
  1963. /**
  1964. * Implement this interface and pass it to Tree.setItemDescriptionGenerator
  1965. * or Table.setItemDescriptionGenerator to generate mouse over descriptions
  1966. * ("tooltips") for the rows and cells in Table or for the items in Tree.
  1967. */
  1968. public interface ItemDescriptionGenerator extends Serializable {
  1969. /**
  1970. * Called by Table when a cell (and row) is painted or a item is painted
  1971. * in Tree
  1972. *
  1973. * @param source
  1974. * The source of the generator, the Tree or Table the
  1975. * generator is attached to
  1976. * @param itemId
  1977. * The itemId of the painted cell
  1978. * @param propertyId
  1979. * The propertyId of the cell, null when getting row
  1980. * description
  1981. * @return The description or "tooltip" of the item.
  1982. */
  1983. public String generateDescription(Component source, Object itemId,
  1984. Object propertyId);
  1985. }
  1986. @Override
  1987. public void readDesign(Element design, DesignContext context) {
  1988. // handle default attributes
  1989. super.readDesign(design, context);
  1990. // handle children specifying selectable items (<option>)
  1991. readItems(design, context);
  1992. }
  1993. protected void readItems(Element design, DesignContext context) {
  1994. Set<String> selected = new HashSet<String>();
  1995. for (Element child : design.children()) {
  1996. readItem(child, selected, context);
  1997. }
  1998. if (!selected.isEmpty()) {
  1999. if (isMultiSelect()) {
  2000. setValue(selected, false, true);
  2001. } else if (selected.size() == 1) {
  2002. setValue(selected.iterator().next(), false, true);
  2003. } else {
  2004. throw new DesignException(
  2005. "Multiple values selected for a single select component");
  2006. }
  2007. }
  2008. }
  2009. /**
  2010. * Reads an Item from a design and inserts it into the data source.
  2011. * Hierarchical select components should override this method to recursively
  2012. * recursively read any child items as well.
  2013. *
  2014. * @since 7.5.0
  2015. * @param child
  2016. * a child element representing the item
  2017. * @param selected
  2018. * A set accumulating selected items. If the item that is read is
  2019. * marked as selected, its item id should be added to this set.
  2020. * @param context
  2021. * the DesignContext instance used in parsing
  2022. * @return the item id of the new item
  2023. *
  2024. * @throws DesignException
  2025. * if the tag name of the {@code child} element is not
  2026. * {@code option}.
  2027. */
  2028. protected Object readItem(Element child, Set<String> selected,
  2029. DesignContext context) {
  2030. if (!"option".equals(child.tagName())) {
  2031. throw new DesignException("Unrecognized child element in "
  2032. + getClass().getSimpleName() + ": " + child.tagName());
  2033. }
  2034. String itemId;
  2035. String caption = DesignFormatter.decodeFromTextNode(child.html());
  2036. if (child.hasAttr("item-id")) {
  2037. itemId = child.attr("item-id");
  2038. addItem(itemId);
  2039. setItemCaption(itemId, caption);
  2040. } else {
  2041. addItem(itemId = caption);
  2042. }
  2043. if (child.hasAttr("icon")) {
  2044. setItemIcon(
  2045. itemId,
  2046. DesignAttributeHandler.readAttribute("icon",
  2047. child.attributes(), Resource.class));
  2048. }
  2049. if (child.hasAttr("selected")) {
  2050. selected.add(itemId);
  2051. }
  2052. return itemId;
  2053. }
  2054. @Override
  2055. public void writeDesign(Element design, DesignContext context) {
  2056. // Write default attributes
  2057. super.writeDesign(design, context);
  2058. // Write options if warranted
  2059. if (context.shouldWriteData(this)) {
  2060. writeItems(design, context);
  2061. }
  2062. }
  2063. /**
  2064. * Writes the data source items to a design. Hierarchical select components
  2065. * should override this method to only write the root items.
  2066. *
  2067. * @since 7.5.0
  2068. * @param design
  2069. * the element into which to insert the items
  2070. * @param context
  2071. * the DesignContext instance used in writing
  2072. */
  2073. protected void writeItems(Element design, DesignContext context) {
  2074. for (Object itemId : getItemIds()) {
  2075. writeItem(design, itemId, context);
  2076. }
  2077. }
  2078. /**
  2079. * Writes a data source Item to a design. Hierarchical select components
  2080. * should override this method to recursively write any child items as well.
  2081. *
  2082. * @since 7.5.0
  2083. * @param design
  2084. * the element into which to insert the item
  2085. * @param itemId
  2086. * the id of the item to write
  2087. * @param context
  2088. * the DesignContext instance used in writing
  2089. * @return
  2090. */
  2091. protected Element writeItem(Element design, Object itemId,
  2092. DesignContext context) {
  2093. Element element = design.appendElement("option");
  2094. String caption = getItemCaption(itemId);
  2095. if (caption != null && !caption.equals(itemId.toString())) {
  2096. element.html(DesignFormatter.encodeForTextNode(caption));
  2097. element.attr("item-id", itemId.toString());
  2098. } else {
  2099. element.html(DesignFormatter.encodeForTextNode(itemId.toString()));
  2100. }
  2101. Resource icon = getItemIcon(itemId);
  2102. if (icon != null) {
  2103. DesignAttributeHandler.writeAttribute("icon", element.attributes(),
  2104. icon, null, Resource.class);
  2105. }
  2106. if (isSelected(itemId)) {
  2107. element.attr("selected", "");
  2108. }
  2109. return element;
  2110. }
  2111. @Override
  2112. protected AbstractSelectState getState() {
  2113. return (AbstractSelectState) super.getState();
  2114. }
  2115. }