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.

Form.java 43KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390
  1. /*
  2. * Copyright 2011 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.Collection;
  19. import java.util.Collections;
  20. import java.util.HashMap;
  21. import java.util.Iterator;
  22. import java.util.LinkedList;
  23. import java.util.Map;
  24. import com.vaadin.data.Buffered;
  25. import com.vaadin.data.Item;
  26. import com.vaadin.data.Property;
  27. import com.vaadin.data.Validatable;
  28. import com.vaadin.data.Validator;
  29. import com.vaadin.data.Validator.InvalidValueException;
  30. import com.vaadin.data.fieldgroup.FieldGroup;
  31. import com.vaadin.data.util.BeanItem;
  32. import com.vaadin.event.Action;
  33. import com.vaadin.event.Action.Handler;
  34. import com.vaadin.event.Action.ShortcutNotifier;
  35. import com.vaadin.event.ActionManager;
  36. import com.vaadin.server.AbstractErrorMessage;
  37. import com.vaadin.server.CompositeErrorMessage;
  38. import com.vaadin.server.ErrorMessage;
  39. import com.vaadin.server.PaintException;
  40. import com.vaadin.server.PaintTarget;
  41. import com.vaadin.server.UserError;
  42. import com.vaadin.shared.ui.form.FormState;
  43. /**
  44. * Form component provides easy way of creating and managing sets fields.
  45. *
  46. * <p>
  47. * <code>Form</code> is a container for fields implementing {@link Field}
  48. * interface. It provides support for any layouts and provides buffering
  49. * interface for easy connection of commit and discard buttons. All the form
  50. * fields can be customized by adding validators, setting captions and icons,
  51. * setting immediateness, etc. Also direct mechanism for replacing existing
  52. * fields with selections is given.
  53. * </p>
  54. *
  55. * <p>
  56. * <code>Form</code> provides customizable editor for classes implementing
  57. * {@link com.vaadin.data.Item} interface. Also the form itself implements this
  58. * interface for easier connectivity to other items. To use the form as editor
  59. * for an item, just connect the item to form with
  60. * {@link Form#setItemDataSource(Item)}. If only a part of the item needs to be
  61. * edited, {@link Form#setItemDataSource(Item,Collection)} can be used instead.
  62. * After the item has been connected to the form, the automatically created
  63. * fields can be customized and new fields can be added. If you need to connect
  64. * a class that does not implement {@link com.vaadin.data.Item} interface, most
  65. * properties of any class following bean pattern, can be accessed trough
  66. * {@link com.vaadin.data.util.BeanItem}.
  67. * </p>
  68. *
  69. * @author Vaadin Ltd.
  70. * @since 3.0
  71. * @deprecated As of 7.0, use {@link FieldGroup} instead of {@link Form} for
  72. * more flexibility.
  73. */
  74. @Deprecated
  75. public class Form extends AbstractField<Object> implements Item.Editor,
  76. Buffered, Item, Validatable, Action.Notifier, HasComponents,
  77. LegacyComponent {
  78. private Object propertyValue;
  79. /**
  80. * Item connected to this form as datasource.
  81. */
  82. private Item itemDatasource;
  83. /**
  84. * Ordered list of property ids in this editor.
  85. */
  86. private final LinkedList<Object> propertyIds = new LinkedList<Object>();
  87. /**
  88. * Current buffered source exception.
  89. */
  90. private Buffered.SourceException currentBufferedSourceException = null;
  91. /**
  92. * Is the form in buffered mode.
  93. */
  94. private boolean buffered = false;
  95. /**
  96. * Mapping from propertyName to corresponding field.
  97. */
  98. private final HashMap<Object, Field<?>> fields = new HashMap<Object, Field<?>>();
  99. /**
  100. * Form may act as an Item, its own properties are stored here.
  101. */
  102. private final HashMap<Object, Property<?>> ownProperties = new HashMap<Object, Property<?>>();
  103. /**
  104. * Field factory for this form.
  105. */
  106. private FormFieldFactory fieldFactory;
  107. /**
  108. * Visible item properties.
  109. */
  110. private Collection<?> visibleItemProperties;
  111. /**
  112. * Form needs to repaint itself if child fields value changes due possible
  113. * change in form validity.
  114. *
  115. * TODO introduce ValidityChangeEvent (#6239) and start using it instead.
  116. * See e.g. DateField#notifyFormOfValidityChange().
  117. */
  118. private final ValueChangeListener fieldValueChangeListener = new ValueChangeListener() {
  119. @Override
  120. public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
  121. markAsDirty();
  122. }
  123. };
  124. /**
  125. * If this is true, commit implicitly calls setValidationVisible(true).
  126. */
  127. private boolean validationVisibleOnCommit = true;
  128. // special handling for gridlayout; remember initial cursor pos
  129. private int gridlayoutCursorX = -1;
  130. private int gridlayoutCursorY = -1;
  131. /**
  132. * Keeps track of the Actions added to this component, and manages the
  133. * painting and handling as well. Note that the extended AbstractField is a
  134. * {@link ShortcutNotifier} and has a actionManager that delegates actions
  135. * to the containing window. This one does not delegate.
  136. */
  137. private ActionManager ownActionManager = new ActionManager(this);
  138. /**
  139. * Constructs a new form with default layout.
  140. *
  141. * <p>
  142. * By default the form uses {@link FormLayout}.
  143. * </p>
  144. */
  145. public Form() {
  146. this(null);
  147. setValidationVisible(false);
  148. }
  149. /**
  150. * Constructs a new form with given {@link Layout}.
  151. *
  152. * @param formLayout
  153. * the layout of the form.
  154. */
  155. public Form(Layout formLayout) {
  156. this(formLayout, DefaultFieldFactory.get());
  157. }
  158. /**
  159. * Constructs a new form with given {@link Layout} and
  160. * {@link FormFieldFactory}.
  161. *
  162. * @param formLayout
  163. * the layout of the form.
  164. * @param fieldFactory
  165. * the FieldFactory of the form.
  166. */
  167. public Form(Layout formLayout, FormFieldFactory fieldFactory) {
  168. super();
  169. setLayout(formLayout);
  170. setFooter(null);
  171. setFormFieldFactory(fieldFactory);
  172. setValidationVisible(false);
  173. setWidth(100, UNITS_PERCENTAGE);
  174. }
  175. @Override
  176. protected FormState getState() {
  177. return (FormState) super.getState();
  178. }
  179. /* Documented in interface */
  180. @Override
  181. public void paintContent(PaintTarget target) throws PaintException {
  182. if (ownActionManager != null) {
  183. ownActionManager.paintActions(null, target);
  184. }
  185. }
  186. @Override
  187. public void changeVariables(Object source, Map<String, Object> variables) {
  188. // Actions
  189. if (ownActionManager != null) {
  190. ownActionManager.handleActions(variables, this);
  191. }
  192. }
  193. /**
  194. * The error message of a Form is the error of the first field with a
  195. * non-empty error.
  196. *
  197. * Empty error messages of the contained fields are skipped, because an
  198. * empty error indicator would be confusing to the user, especially if there
  199. * are errors that have something to display. This is also the reason why
  200. * the calculation of the error message is separate from validation, because
  201. * validation fails also on empty errors.
  202. */
  203. @Override
  204. public ErrorMessage getErrorMessage() {
  205. // Reimplement the checking of validation error by using
  206. // getErrorMessage() recursively instead of validate().
  207. ErrorMessage validationError = null;
  208. if (isValidationVisible()) {
  209. for (final Iterator<Object> i = propertyIds.iterator(); i.hasNext();) {
  210. Object f = fields.get(i.next());
  211. if (f instanceof AbstractComponent) {
  212. AbstractComponent field = (AbstractComponent) f;
  213. validationError = field.getErrorMessage();
  214. if (validationError != null) {
  215. // Show caption as error for fields with empty errors
  216. if ("".equals(validationError.toString())) {
  217. validationError = new UserError(field.getCaption());
  218. }
  219. break;
  220. } else if (f instanceof Field && !((Field<?>) f).isValid()) {
  221. // Something is wrong with the field, but no proper
  222. // error is given. Generate one.
  223. validationError = new UserError(field.getCaption());
  224. break;
  225. }
  226. }
  227. }
  228. }
  229. // Return if there are no errors at all
  230. if (getComponentError() == null && validationError == null
  231. && currentBufferedSourceException == null) {
  232. return null;
  233. }
  234. // Throw combination of the error types
  235. return new CompositeErrorMessage(
  236. new ErrorMessage[] {
  237. getComponentError(),
  238. validationError,
  239. AbstractErrorMessage
  240. .getErrorMessageForException(currentBufferedSourceException) });
  241. }
  242. /**
  243. * Controls the making validation visible implicitly on commit.
  244. *
  245. * Having commit() call setValidationVisible(true) implicitly is the default
  246. * behaviour. You can disable the implicit setting by setting this property
  247. * as false.
  248. *
  249. * It is useful, because you usually want to start with the form free of
  250. * errors and only display them after the user clicks Ok. You can disable
  251. * the implicit setting by setting this property as false.
  252. *
  253. * @param makeVisible
  254. * If true (default), validation is made visible when commit() is
  255. * called. If false, the visibility is left as it is.
  256. */
  257. public void setValidationVisibleOnCommit(boolean makeVisible) {
  258. validationVisibleOnCommit = makeVisible;
  259. }
  260. /**
  261. * Is validation made automatically visible on commit?
  262. *
  263. * See setValidationVisibleOnCommit().
  264. *
  265. * @return true if validation is made automatically visible on commit.
  266. */
  267. public boolean isValidationVisibleOnCommit() {
  268. return validationVisibleOnCommit;
  269. }
  270. /*
  271. * Commit changes to the data source Don't add a JavaDoc comment here, we
  272. * use the default one from the interface.
  273. */
  274. @Override
  275. public void commit() throws Buffered.SourceException, InvalidValueException {
  276. LinkedList<SourceException> problems = null;
  277. // Only commit on valid state if so requested
  278. if (!isInvalidCommitted() && !isValid()) {
  279. /*
  280. * The values are not ok and we are told not to commit invalid
  281. * values
  282. */
  283. if (validationVisibleOnCommit) {
  284. setValidationVisible(true);
  285. }
  286. // Find the first invalid value and throw the exception
  287. validate();
  288. }
  289. // Try to commit all
  290. for (final Iterator<Object> i = propertyIds.iterator(); i.hasNext();) {
  291. try {
  292. final Field<?> f = (fields.get(i.next()));
  293. // Commit only non-readonly fields.
  294. if (!f.isReadOnly()) {
  295. f.commit();
  296. }
  297. } catch (final Buffered.SourceException e) {
  298. if (problems == null) {
  299. problems = new LinkedList<SourceException>();
  300. }
  301. problems.add(e);
  302. }
  303. }
  304. // No problems occurred
  305. if (problems == null) {
  306. if (currentBufferedSourceException != null) {
  307. currentBufferedSourceException = null;
  308. markAsDirty();
  309. }
  310. return;
  311. }
  312. // Commit problems
  313. final Throwable[] causes = new Throwable[problems.size()];
  314. int index = 0;
  315. for (final Iterator<SourceException> i = problems.iterator(); i
  316. .hasNext();) {
  317. causes[index++] = i.next();
  318. }
  319. final Buffered.SourceException e = new Buffered.SourceException(this,
  320. causes);
  321. currentBufferedSourceException = e;
  322. markAsDirty();
  323. throw e;
  324. }
  325. /*
  326. * Discards local changes and refresh values from the data source Don't add
  327. * a JavaDoc comment here, we use the default one from the interface.
  328. */
  329. @Override
  330. public void discard() throws Buffered.SourceException {
  331. LinkedList<SourceException> problems = null;
  332. // Try to discard all changes
  333. for (final Iterator<Object> i = propertyIds.iterator(); i.hasNext();) {
  334. try {
  335. (fields.get(i.next())).discard();
  336. } catch (final Buffered.SourceException e) {
  337. if (problems == null) {
  338. problems = new LinkedList<SourceException>();
  339. }
  340. problems.add(e);
  341. }
  342. }
  343. // No problems occurred
  344. if (problems == null) {
  345. if (currentBufferedSourceException != null) {
  346. currentBufferedSourceException = null;
  347. markAsDirty();
  348. }
  349. return;
  350. }
  351. // Discards problems occurred
  352. final Throwable[] causes = new Throwable[problems.size()];
  353. int index = 0;
  354. for (final Iterator<SourceException> i = problems.iterator(); i
  355. .hasNext();) {
  356. causes[index++] = i.next();
  357. }
  358. final Buffered.SourceException e = new Buffered.SourceException(this,
  359. causes);
  360. currentBufferedSourceException = e;
  361. markAsDirty();
  362. throw e;
  363. }
  364. /*
  365. * Is the object modified but not committed? Don't add a JavaDoc comment
  366. * here, we use the default one from the interface.
  367. */
  368. @Override
  369. public boolean isModified() {
  370. for (final Iterator<Object> i = propertyIds.iterator(); i.hasNext();) {
  371. final Field<?> f = fields.get(i.next());
  372. if (f != null && f.isModified()) {
  373. return true;
  374. }
  375. }
  376. return false;
  377. }
  378. /*
  379. * Sets the editor's buffered mode to the specified status. Don't add a
  380. * JavaDoc comment here, we use the default one from the interface.
  381. */
  382. @Override
  383. public void setBuffered(boolean buffered) {
  384. if (buffered != this.buffered) {
  385. this.buffered = buffered;
  386. for (final Iterator<Object> i = propertyIds.iterator(); i.hasNext();) {
  387. (fields.get(i.next())).setBuffered(buffered);
  388. }
  389. }
  390. }
  391. /**
  392. * Adds a new property to form and create corresponding field.
  393. *
  394. * @see com.vaadin.data.Item#addItemProperty(Object, Property)
  395. */
  396. @Override
  397. public boolean addItemProperty(Object id, Property property) {
  398. // Checks inputs
  399. if (id == null || property == null) {
  400. throw new NullPointerException("Id and property must be non-null");
  401. }
  402. // Checks that the property id is not reserved
  403. if (propertyIds.contains(id)) {
  404. return false;
  405. }
  406. propertyIds.add(id);
  407. ownProperties.put(id, property);
  408. // Gets suitable field
  409. final Field<?> field = fieldFactory.createField(this, id, this);
  410. if (field == null) {
  411. return false;
  412. }
  413. // Configures the field
  414. bindPropertyToField(id, property, field);
  415. // Register and attach the created field
  416. addField(id, field);
  417. return true;
  418. }
  419. /**
  420. * Registers the field with the form and adds the field to the form layout.
  421. *
  422. * <p>
  423. * The property id must not be already used in the form.
  424. * </p>
  425. *
  426. * <p>
  427. * This field is added to the layout using the
  428. * {@link #attachField(Object, Field)} method.
  429. * </p>
  430. *
  431. * @param propertyId
  432. * the Property id the the field.
  433. * @param field
  434. * the field which should be added to the form.
  435. */
  436. public void addField(Object propertyId, Field<?> field) {
  437. registerField(propertyId, field);
  438. attachField(propertyId, field);
  439. markAsDirty();
  440. }
  441. /**
  442. * Register the field with the form. All registered fields are validated
  443. * when the form is validated and also committed when the form is committed.
  444. *
  445. * <p>
  446. * The property id must not be already used in the form.
  447. * </p>
  448. *
  449. *
  450. * @param propertyId
  451. * the Property id of the field.
  452. * @param field
  453. * the Field that should be registered
  454. */
  455. private void registerField(Object propertyId, Field<?> field) {
  456. if (propertyId == null || field == null) {
  457. return;
  458. }
  459. fields.put(propertyId, field);
  460. field.addListener(fieldValueChangeListener);
  461. if (!propertyIds.contains(propertyId)) {
  462. // adding a field directly
  463. propertyIds.addLast(propertyId);
  464. }
  465. // Update the buffered mode and immediate to match the
  466. // form.
  467. // Should this also include invalidCommitted (#3993)?
  468. field.setBuffered(buffered);
  469. if (isImmediate() && field instanceof AbstractComponent) {
  470. ((AbstractComponent) field).setImmediate(true);
  471. }
  472. }
  473. /**
  474. * Adds the field to the form layout.
  475. * <p>
  476. * The field is added to the form layout in the default position (the
  477. * position used by {@link Layout#addComponent(Component)}. If the
  478. * underlying layout is a {@link CustomLayout} the field is added to the
  479. * CustomLayout location given by the string representation of the property
  480. * id using {@link CustomLayout#addComponent(Component, String)}.
  481. * </p>
  482. *
  483. * <p>
  484. * Override this method to control how the fields are added to the layout.
  485. * </p>
  486. *
  487. * @param propertyId
  488. * @param field
  489. */
  490. protected void attachField(Object propertyId, Field field) {
  491. if (propertyId == null || field == null) {
  492. return;
  493. }
  494. Layout layout = getLayout();
  495. if (layout instanceof CustomLayout) {
  496. ((CustomLayout) layout).addComponent(field, propertyId.toString());
  497. } else {
  498. layout.addComponent(field);
  499. }
  500. }
  501. /**
  502. * The property identified by the property id.
  503. *
  504. * <p>
  505. * The property data source of the field specified with property id is
  506. * returned. If there is a (with specified property id) having no data
  507. * source, the field is returned instead of the data source.
  508. * </p>
  509. *
  510. * @see com.vaadin.data.Item#getItemProperty(Object)
  511. */
  512. @Override
  513. public Property getItemProperty(Object id) {
  514. final Field<?> field = fields.get(id);
  515. if (field == null) {
  516. // field does not exist or it is not (yet) created for this property
  517. return ownProperties.get(id);
  518. }
  519. final Property<?> property = field.getPropertyDataSource();
  520. if (property != null) {
  521. return property;
  522. } else {
  523. return field;
  524. }
  525. }
  526. /**
  527. * Gets the field identified by the propertyid.
  528. *
  529. * @param propertyId
  530. * the id of the property.
  531. */
  532. public Field getField(Object propertyId) {
  533. return fields.get(propertyId);
  534. }
  535. /* Documented in interface */
  536. @Override
  537. public Collection<?> getItemPropertyIds() {
  538. return Collections.unmodifiableCollection(propertyIds);
  539. }
  540. /**
  541. * Removes the property and corresponding field from the form.
  542. *
  543. * @see com.vaadin.data.Item#removeItemProperty(Object)
  544. */
  545. @Override
  546. public boolean removeItemProperty(Object id) {
  547. ownProperties.remove(id);
  548. final Field<?> field = fields.get(id);
  549. if (field != null) {
  550. propertyIds.remove(id);
  551. fields.remove(id);
  552. detachField(field);
  553. field.removeListener(fieldValueChangeListener);
  554. return true;
  555. }
  556. return false;
  557. }
  558. /**
  559. * Called when a form field is detached from a Form. Typically when a new
  560. * Item is assigned to Form via {@link #setItemDataSource(Item)}.
  561. * <p>
  562. * Override this method to control how the fields are removed from the
  563. * layout.
  564. * </p>
  565. *
  566. * @param field
  567. * the field to be detached from the forms layout.
  568. */
  569. protected void detachField(final Field field) {
  570. Component p = field.getParent();
  571. if (p instanceof ComponentContainer) {
  572. ((ComponentContainer) p).removeComponent(field);
  573. }
  574. }
  575. /**
  576. * Removes all properties and fields from the form.
  577. *
  578. * @return the Success of the operation. Removal of all fields succeeded if
  579. * (and only if) the return value is <code>true</code>.
  580. */
  581. public boolean removeAllProperties() {
  582. final Object[] properties = propertyIds.toArray();
  583. boolean success = true;
  584. for (int i = 0; i < properties.length; i++) {
  585. if (!removeItemProperty(properties[i])) {
  586. success = false;
  587. }
  588. }
  589. return success;
  590. }
  591. /* Documented in the interface */
  592. @Override
  593. public Item getItemDataSource() {
  594. return itemDatasource;
  595. }
  596. /**
  597. * Sets the item datasource for the form.
  598. *
  599. * <p>
  600. * Setting item datasource clears any fields, the form might contain and
  601. * adds all the properties as fields to the form.
  602. * </p>
  603. *
  604. * @see com.vaadin.data.Item.Viewer#setItemDataSource(Item)
  605. */
  606. @Override
  607. public void setItemDataSource(Item newDataSource) {
  608. setItemDataSource(newDataSource,
  609. newDataSource != null ? newDataSource.getItemPropertyIds()
  610. : null);
  611. }
  612. /**
  613. * Set the item datasource for the form, but limit the form contents to
  614. * specified properties of the item.
  615. *
  616. * <p>
  617. * Setting item datasource clears any fields, the form might contain and
  618. * adds the specified the properties as fields to the form, in the specified
  619. * order.
  620. * </p>
  621. *
  622. * @see com.vaadin.data.Item.Viewer#setItemDataSource(Item)
  623. */
  624. public void setItemDataSource(Item newDataSource, Collection<?> propertyIds) {
  625. if (getLayout() instanceof GridLayout) {
  626. GridLayout gl = (GridLayout) getLayout();
  627. if (gridlayoutCursorX == -1) {
  628. // first setItemDataSource, remember initial cursor
  629. gridlayoutCursorX = gl.getCursorX();
  630. gridlayoutCursorY = gl.getCursorY();
  631. } else {
  632. // restore initial cursor
  633. gl.setCursorX(gridlayoutCursorX);
  634. gl.setCursorY(gridlayoutCursorY);
  635. }
  636. }
  637. // Removes all fields first from the form
  638. removeAllProperties();
  639. // Sets the datasource
  640. itemDatasource = newDataSource;
  641. // If the new datasource is null, just set null datasource
  642. if (itemDatasource == null) {
  643. markAsDirty();
  644. return;
  645. }
  646. // Adds all the properties to this form
  647. for (final Iterator<?> i = propertyIds.iterator(); i.hasNext();) {
  648. final Object id = i.next();
  649. final Property<?> property = itemDatasource.getItemProperty(id);
  650. if (id != null && property != null) {
  651. final Field<?> f = fieldFactory.createField(itemDatasource, id,
  652. this);
  653. if (f != null) {
  654. bindPropertyToField(id, property, f);
  655. addField(id, f);
  656. }
  657. }
  658. }
  659. }
  660. /**
  661. * Binds an item property to a field. The default behavior is to bind
  662. * property straight to Field. If Property.Viewer type property (e.g.
  663. * PropertyFormatter) is already set for field, the property is bound to
  664. * that Property.Viewer.
  665. *
  666. * @param propertyId
  667. * @param property
  668. * @param field
  669. * @since 6.7.3
  670. */
  671. protected void bindPropertyToField(final Object propertyId,
  672. final Property property, final Field field) {
  673. // check if field has a property that is Viewer set. In that case we
  674. // expect developer has e.g. PropertyFormatter that he wishes to use and
  675. // assign the property to the Viewer instead.
  676. boolean hasFilterProperty = field.getPropertyDataSource() != null
  677. && (field.getPropertyDataSource() instanceof Property.Viewer);
  678. if (hasFilterProperty) {
  679. ((Property.Viewer) field.getPropertyDataSource())
  680. .setPropertyDataSource(property);
  681. } else {
  682. field.setPropertyDataSource(property);
  683. }
  684. }
  685. /**
  686. * Gets the layout of the form.
  687. *
  688. * <p>
  689. * By default form uses <code>OrderedLayout</code> with <code>form</code>
  690. * -style.
  691. * </p>
  692. *
  693. * @return the Layout of the form.
  694. */
  695. public Layout getLayout() {
  696. return (Layout) getState().layout;
  697. }
  698. /**
  699. * Sets the layout of the form.
  700. *
  701. * <p>
  702. * If set to null then Form uses a FormLayout by default.
  703. * </p>
  704. *
  705. * @param layout
  706. * the layout of the form.
  707. */
  708. public void setLayout(Layout layout) {
  709. // Use orderedlayout by default
  710. if (layout == null) {
  711. layout = new FormLayout();
  712. }
  713. // reset cursor memory
  714. gridlayoutCursorX = -1;
  715. gridlayoutCursorY = -1;
  716. // Move fields from previous layout
  717. if (getLayout() != null) {
  718. final Object[] properties = propertyIds.toArray();
  719. for (int i = 0; i < properties.length; i++) {
  720. Field<?> f = getField(properties[i]);
  721. detachField(f);
  722. if (layout instanceof CustomLayout) {
  723. ((CustomLayout) layout).addComponent(f,
  724. properties[i].toString());
  725. } else {
  726. layout.addComponent(f);
  727. }
  728. }
  729. getLayout().setParent(null);
  730. }
  731. // Replace the previous layout
  732. layout.setParent(this);
  733. getState().layout = layout;
  734. }
  735. /**
  736. * Sets the form field to be selectable from static list of changes.
  737. *
  738. * <p>
  739. * The list values and descriptions are given as array. The value-array must
  740. * contain the current value of the field and the lengths of the arrays must
  741. * match. Null values are not supported.
  742. * </p>
  743. *
  744. * Note: since Vaadin 7.0, returns an {@link AbstractSelect} instead of a
  745. * {@link Select}.
  746. *
  747. * @param propertyId
  748. * the id of the property.
  749. * @param values
  750. * @param descriptions
  751. * @return the select property generated
  752. */
  753. public AbstractSelect replaceWithSelect(Object propertyId, Object[] values,
  754. Object[] descriptions) {
  755. // Checks the parameters
  756. if (propertyId == null || values == null || descriptions == null) {
  757. throw new NullPointerException("All parameters must be non-null");
  758. }
  759. if (values.length != descriptions.length) {
  760. throw new IllegalArgumentException(
  761. "Value and description list are of different size");
  762. }
  763. // Gets the old field
  764. final Field<?> oldField = fields.get(propertyId);
  765. if (oldField == null) {
  766. throw new IllegalArgumentException("Field with given propertyid '"
  767. + propertyId.toString() + "' can not be found.");
  768. }
  769. final Object value = oldField.getPropertyDataSource() == null ? oldField
  770. .getValue() : oldField.getPropertyDataSource().getValue();
  771. // Checks that the value exists and check if the select should
  772. // be forced in multiselect mode
  773. boolean found = false;
  774. boolean isMultiselect = false;
  775. for (int i = 0; i < values.length && !found; i++) {
  776. if (values[i] == value
  777. || (value != null && value.equals(values[i]))) {
  778. found = true;
  779. }
  780. }
  781. if (value != null && !found) {
  782. if (value instanceof Collection) {
  783. for (final Iterator<?> it = ((Collection<?>) value).iterator(); it
  784. .hasNext();) {
  785. final Object val = it.next();
  786. found = false;
  787. for (int i = 0; i < values.length && !found; i++) {
  788. if (values[i] == val
  789. || (val != null && val.equals(values[i]))) {
  790. found = true;
  791. }
  792. }
  793. if (!found) {
  794. throw new IllegalArgumentException(
  795. "Currently selected value '" + val
  796. + "' of property '"
  797. + propertyId.toString()
  798. + "' was not found");
  799. }
  800. }
  801. isMultiselect = true;
  802. } else {
  803. throw new IllegalArgumentException("Current value '" + value
  804. + "' of property '" + propertyId.toString()
  805. + "' was not found");
  806. }
  807. }
  808. // Creates the new field matching to old field parameters
  809. final AbstractSelect newField = isMultiselect ? new ListSelect()
  810. : new Select();
  811. newField.setCaption(oldField.getCaption());
  812. newField.setReadOnly(oldField.isReadOnly());
  813. newField.setBuffered(oldField.isBuffered());
  814. // Creates the options list
  815. newField.addContainerProperty("desc", String.class, "");
  816. newField.setItemCaptionPropertyId("desc");
  817. for (int i = 0; i < values.length; i++) {
  818. Object id = values[i];
  819. final Item item;
  820. if (id == null) {
  821. id = newField.addItem();
  822. item = newField.getItem(id);
  823. newField.setNullSelectionItemId(id);
  824. } else {
  825. item = newField.addItem(id);
  826. }
  827. if (item != null) {
  828. item.getItemProperty("desc").setValue(
  829. descriptions[i].toString());
  830. }
  831. }
  832. // Sets the property data source
  833. final Property<?> property = oldField.getPropertyDataSource();
  834. oldField.setPropertyDataSource(null);
  835. newField.setPropertyDataSource(property);
  836. // Replaces the old field with new one
  837. getLayout().replaceComponent(oldField, newField);
  838. fields.put(propertyId, newField);
  839. newField.addListener(fieldValueChangeListener);
  840. oldField.removeListener(fieldValueChangeListener);
  841. return newField;
  842. }
  843. /**
  844. * Checks the validity of the Form and all of its fields.
  845. *
  846. * @see com.vaadin.data.Validatable#validate()
  847. */
  848. @Override
  849. public void validate() throws InvalidValueException {
  850. super.validate();
  851. for (final Iterator<Object> i = propertyIds.iterator(); i.hasNext();) {
  852. (fields.get(i.next())).validate();
  853. }
  854. }
  855. /**
  856. * Checks the validabtable object accept invalid values.
  857. *
  858. * @see com.vaadin.data.Validatable#isInvalidAllowed()
  859. */
  860. @Override
  861. public boolean isInvalidAllowed() {
  862. return true;
  863. }
  864. /**
  865. * Should the validabtable object accept invalid values.
  866. *
  867. * @see com.vaadin.data.Validatable#setInvalidAllowed(boolean)
  868. */
  869. @Override
  870. public void setInvalidAllowed(boolean invalidValueAllowed)
  871. throws UnsupportedOperationException {
  872. throw new UnsupportedOperationException();
  873. }
  874. /**
  875. * Sets the component's to read-only mode to the specified state.
  876. *
  877. * @see com.vaadin.ui.Component#setReadOnly(boolean)
  878. */
  879. @Override
  880. public void setReadOnly(boolean readOnly) {
  881. super.setReadOnly(readOnly);
  882. for (final Iterator<?> i = propertyIds.iterator(); i.hasNext();) {
  883. (fields.get(i.next())).setReadOnly(readOnly);
  884. }
  885. }
  886. /**
  887. * Sets the field factory used by this Form to genarate Fields for
  888. * properties.
  889. *
  890. * {@link FormFieldFactory} is used to create fields for form properties.
  891. * {@link DefaultFieldFactory} is used by default.
  892. *
  893. * @param fieldFactory
  894. * the new factory used to create the fields.
  895. * @see Field
  896. * @see FormFieldFactory
  897. */
  898. public void setFormFieldFactory(FormFieldFactory fieldFactory) {
  899. this.fieldFactory = fieldFactory;
  900. }
  901. /**
  902. * Get the field factory of the form.
  903. *
  904. * @return the FormFieldFactory Factory used to create the fields.
  905. */
  906. public FormFieldFactory getFormFieldFactory() {
  907. return fieldFactory;
  908. }
  909. /**
  910. * Gets the field type.
  911. *
  912. * @see com.vaadin.ui.AbstractField#getType()
  913. */
  914. @Override
  915. public Class<?> getType() {
  916. if (getPropertyDataSource() != null) {
  917. return getPropertyDataSource().getType();
  918. }
  919. return Object.class;
  920. }
  921. /**
  922. * Sets the internal value.
  923. *
  924. * This is relevant when the Form is used as Field.
  925. *
  926. * @see com.vaadin.ui.AbstractField#setInternalValue(java.lang.Object)
  927. */
  928. @Override
  929. protected void setInternalValue(Object newValue) {
  930. // Stores the old value
  931. final Object oldValue = propertyValue;
  932. // Sets the current Value
  933. super.setInternalValue(newValue);
  934. propertyValue = newValue;
  935. // Ignores form updating if data object has not changed.
  936. if (oldValue != newValue) {
  937. setFormDataSource(newValue, getVisibleItemProperties());
  938. }
  939. }
  940. /**
  941. * Gets the first focusable field in form. If there are enabled,
  942. * non-read-only fields, the first one of them is returned. Otherwise, the
  943. * field for the first property (or null if none) is returned.
  944. *
  945. * @return the Field.
  946. */
  947. private Field<?> getFirstFocusableField() {
  948. if (getItemPropertyIds() != null) {
  949. for (Object id : getItemPropertyIds()) {
  950. if (id != null) {
  951. Field<?> field = getField(id);
  952. if (field.isEnabled() && !field.isReadOnly()) {
  953. return field;
  954. }
  955. }
  956. }
  957. // fallback: first field if none of the fields is enabled and
  958. // writable
  959. Object id = getItemPropertyIds().iterator().next();
  960. if (id != null) {
  961. return getField(id);
  962. }
  963. }
  964. return null;
  965. }
  966. /**
  967. * Updates the internal form datasource.
  968. *
  969. * Method setFormDataSource.
  970. *
  971. * @param data
  972. * @param properties
  973. */
  974. protected void setFormDataSource(Object data, Collection<?> properties) {
  975. // If data is an item use it.
  976. Item item = null;
  977. if (data instanceof Item) {
  978. item = (Item) data;
  979. } else if (data != null) {
  980. item = new BeanItem<Object>(data);
  981. }
  982. // Sets the datasource to form
  983. if (item != null && properties != null) {
  984. // Shows only given properties
  985. this.setItemDataSource(item, properties);
  986. } else {
  987. // Shows all properties
  988. this.setItemDataSource(item);
  989. }
  990. }
  991. /**
  992. * Returns the visibleProperties.
  993. *
  994. * @return the Collection of visible Item properites.
  995. */
  996. public Collection<?> getVisibleItemProperties() {
  997. return visibleItemProperties;
  998. }
  999. /**
  1000. * Sets the visibleProperties.
  1001. *
  1002. * @param visibleProperties
  1003. * the visibleProperties to set.
  1004. */
  1005. public void setVisibleItemProperties(Collection<?> visibleProperties) {
  1006. visibleItemProperties = visibleProperties;
  1007. Object value = getValue();
  1008. if (value == null) {
  1009. value = itemDatasource;
  1010. }
  1011. setFormDataSource(value, getVisibleItemProperties());
  1012. }
  1013. /**
  1014. * Sets the visibleProperties.
  1015. *
  1016. * @param visibleProperties
  1017. * the visibleProperties to set.
  1018. */
  1019. public void setVisibleItemProperties(Object[] visibleProperties) {
  1020. LinkedList<Object> v = new LinkedList<Object>();
  1021. for (int i = 0; i < visibleProperties.length; i++) {
  1022. v.add(visibleProperties[i]);
  1023. }
  1024. setVisibleItemProperties(v);
  1025. }
  1026. /**
  1027. * Focuses the first field in the form.
  1028. *
  1029. * @see com.vaadin.ui.Component.Focusable#focus()
  1030. */
  1031. @Override
  1032. public void focus() {
  1033. final Field<?> f = getFirstFocusableField();
  1034. if (f != null) {
  1035. f.focus();
  1036. }
  1037. }
  1038. /**
  1039. * Sets the Tabulator index of this Focusable component.
  1040. *
  1041. * @see com.vaadin.ui.Component.Focusable#setTabIndex(int)
  1042. */
  1043. @Override
  1044. public void setTabIndex(int tabIndex) {
  1045. super.setTabIndex(tabIndex);
  1046. for (final Iterator<?> i = getItemPropertyIds().iterator(); i.hasNext();) {
  1047. (getField(i.next())).setTabIndex(tabIndex);
  1048. }
  1049. }
  1050. /**
  1051. * Setting the form to be immediate also sets all the fields of the form to
  1052. * the same state.
  1053. */
  1054. @Override
  1055. public void setImmediate(boolean immediate) {
  1056. super.setImmediate(immediate);
  1057. for (Iterator<Field<?>> i = fields.values().iterator(); i.hasNext();) {
  1058. Field<?> f = i.next();
  1059. if (f instanceof AbstractComponent) {
  1060. ((AbstractComponent) f).setImmediate(immediate);
  1061. }
  1062. }
  1063. }
  1064. /** Form is empty if all of its fields are empty. */
  1065. @Override
  1066. protected boolean isEmpty() {
  1067. for (Iterator<Field<?>> i = fields.values().iterator(); i.hasNext();) {
  1068. Field<?> f = i.next();
  1069. if (f instanceof AbstractField) {
  1070. if (!((AbstractField<?>) f).isEmpty()) {
  1071. return false;
  1072. }
  1073. }
  1074. }
  1075. return true;
  1076. }
  1077. /**
  1078. * Adding validators directly to form is not supported.
  1079. *
  1080. * Add the validators to form fields instead.
  1081. */
  1082. @Override
  1083. public void addValidator(Validator validator) {
  1084. throw new UnsupportedOperationException();
  1085. }
  1086. /**
  1087. * Returns a layout that is rendered below normal form contents. This area
  1088. * can be used for example to include buttons related to form contents.
  1089. *
  1090. * @return layout rendered below normal form contents.
  1091. */
  1092. public Layout getFooter() {
  1093. return (Layout) getState().footer;
  1094. }
  1095. /**
  1096. * Sets the layout that is rendered below normal form contents. Setting this
  1097. * to null will cause an empty HorizontalLayout to be rendered in the
  1098. * footer.
  1099. *
  1100. * @param footer
  1101. * the new footer layout
  1102. */
  1103. public void setFooter(Layout footer) {
  1104. if (getFooter() != null) {
  1105. getFooter().setParent(null);
  1106. }
  1107. if (footer == null) {
  1108. footer = new HorizontalLayout();
  1109. }
  1110. getState().footer = footer;
  1111. footer.setParent(this);
  1112. }
  1113. @Override
  1114. public void setEnabled(boolean enabled) {
  1115. super.setEnabled(enabled);
  1116. if (getParent() != null && !getParent().isEnabled()) {
  1117. // some ancestor still disabled, don't update children
  1118. return;
  1119. } else {
  1120. getLayout().markAsDirtyRecursive();
  1121. }
  1122. }
  1123. /*
  1124. * ACTIONS
  1125. */
  1126. /**
  1127. * Gets the {@link ActionManager} responsible for handling {@link Action}s
  1128. * added to this Form.<br/>
  1129. * Note that Form has another ActionManager inherited from
  1130. * {@link AbstractField}. The ownActionManager handles Actions attached to
  1131. * this Form specifically, while the ActionManager in AbstractField
  1132. * delegates to the containing Window (i.e global Actions).
  1133. *
  1134. * @return
  1135. */
  1136. protected ActionManager getOwnActionManager() {
  1137. if (ownActionManager == null) {
  1138. ownActionManager = new ActionManager(this);
  1139. }
  1140. return ownActionManager;
  1141. }
  1142. @Override
  1143. public void addActionHandler(Handler actionHandler) {
  1144. getOwnActionManager().addActionHandler(actionHandler);
  1145. }
  1146. @Override
  1147. public void removeActionHandler(Handler actionHandler) {
  1148. if (ownActionManager != null) {
  1149. ownActionManager.removeActionHandler(actionHandler);
  1150. }
  1151. }
  1152. /**
  1153. * Removes all action handlers
  1154. */
  1155. public void removeAllActionHandlers() {
  1156. if (ownActionManager != null) {
  1157. ownActionManager.removeAllActionHandlers();
  1158. }
  1159. }
  1160. @Override
  1161. public <T extends Action & com.vaadin.event.Action.Listener> void addAction(
  1162. T action) {
  1163. getOwnActionManager().addAction(action);
  1164. }
  1165. @Override
  1166. public <T extends Action & com.vaadin.event.Action.Listener> void removeAction(
  1167. T action) {
  1168. if (ownActionManager != null) {
  1169. ownActionManager.removeAction(action);
  1170. }
  1171. }
  1172. @Override
  1173. public Iterator<Component> iterator() {
  1174. return new ComponentIterator();
  1175. }
  1176. /**
  1177. * Modifiable and Serializable Iterator for the components, used by
  1178. * {@link Form#getComponentIterator()}.
  1179. */
  1180. private class ComponentIterator implements Iterator<Component>,
  1181. Serializable {
  1182. int i = 0;
  1183. @Override
  1184. public boolean hasNext() {
  1185. if (i < getComponentCount()) {
  1186. return true;
  1187. }
  1188. return false;
  1189. }
  1190. @Override
  1191. public Component next() {
  1192. if (!hasNext()) {
  1193. return null;
  1194. }
  1195. i++;
  1196. if (i == 1) {
  1197. return getLayout() != null ? getLayout() : getFooter();
  1198. } else if (i == 2) {
  1199. return getFooter();
  1200. }
  1201. return null;
  1202. }
  1203. @Override
  1204. public void remove() {
  1205. if (i == 1) {
  1206. if (getLayout() != null) {
  1207. setLayout(null);
  1208. i = 0;
  1209. } else {
  1210. setFooter(null);
  1211. }
  1212. } else if (i == 2) {
  1213. setFooter(null);
  1214. }
  1215. }
  1216. }
  1217. /**
  1218. * @deprecated As of 7.0, use {@link #iterator()} instead.
  1219. */
  1220. @Deprecated
  1221. public Iterator<Component> getComponentIterator() {
  1222. return iterator();
  1223. }
  1224. public int getComponentCount() {
  1225. int count = 0;
  1226. if (getLayout() != null) {
  1227. count++;
  1228. }
  1229. if (getFooter() != null) {
  1230. count++;
  1231. }
  1232. return count;
  1233. }
  1234. @Override
  1235. public void setVisible(boolean visible) {
  1236. if (isVisible() == visible) {
  1237. return;
  1238. }
  1239. super.setVisible(visible);
  1240. // If the visibility state is toggled it might affect all children
  1241. // aswell, e.g. make container visible should make children visible if
  1242. // they were only hidden because the container was hidden.
  1243. markAsDirtyRecursive();
  1244. }
  1245. }