選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

Form.java 42KB

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