12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397 |
- /*
- * Copyright 2000-2016 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
- package com.vaadin.v7.ui;
-
- import java.io.Serializable;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.LinkedList;
- import java.util.Map;
-
- import com.vaadin.event.Action;
- import com.vaadin.event.Action.Handler;
- import com.vaadin.event.Action.ShortcutNotifier;
- import com.vaadin.event.ActionManager;
- import com.vaadin.server.AbstractErrorMessage;
- import com.vaadin.server.CompositeErrorMessage;
- import com.vaadin.server.ErrorMessage;
- import com.vaadin.server.PaintException;
- import com.vaadin.server.PaintTarget;
- import com.vaadin.server.UserError;
- import com.vaadin.ui.AbstractComponent;
- import com.vaadin.ui.Component;
- import com.vaadin.ui.ComponentContainer;
- import com.vaadin.ui.CustomLayout;
- import com.vaadin.ui.FormLayout;
- import com.vaadin.ui.GridLayout;
- import com.vaadin.ui.HasComponents;
- import com.vaadin.ui.HorizontalLayout;
- import com.vaadin.ui.Layout;
- import com.vaadin.ui.LegacyComponent;
- import com.vaadin.v7.data.Buffered;
- import com.vaadin.v7.data.Item;
- import com.vaadin.v7.data.Property;
- import com.vaadin.v7.data.Validatable;
- import com.vaadin.v7.data.Validator;
- import com.vaadin.v7.data.Validator.InvalidValueException;
- import com.vaadin.v7.data.fieldgroup.FieldGroup;
- import com.vaadin.v7.data.util.BeanItem;
- import com.vaadin.v7.shared.form.FormState;
-
- /**
- * Form component provides easy way of creating and managing sets fields.
- *
- * <p>
- * <code>Form</code> is a container for fields implementing {@link Field}
- * interface. It provides support for any layouts and provides buffering
- * interface for easy connection of commit and discard buttons. All the form
- * fields can be customized by adding validators, setting captions and icons,
- * setting immediateness, etc. Also direct mechanism for replacing existing
- * fields with selections is given.
- * </p>
- *
- * <p>
- * <code>Form</code> provides customizable editor for classes implementing
- * {@link Item} interface. Also the form itself implements this interface for
- * easier connectivity to other items. To use the form as editor for an item,
- * just connect the item to form with {@link Form#setItemDataSource(Item)}. If
- * only a part of the item needs to be edited,
- * {@link Form#setItemDataSource(Item,Collection)} can be used instead. After
- * the item has been connected to the form, the automatically created fields can
- * be customized and new fields can be added. If you need to connect a class
- * that does not implement {@link Item} interface, most properties of any class
- * following bean pattern, can be accessed trough {@link BeanItem}.
- * </p>
- *
- * @author Vaadin Ltd.
- * @since 3.0
- * @deprecated As of 7.0, use {@link FieldGroup} instead of {@link Form} for
- * more flexibility.
- */
- @Deprecated
- public class Form extends AbstractField<Object>
- implements Item.Editor, Buffered, Item, Validatable, Action.Notifier,
- HasComponents, LegacyComponent {
-
- private Object propertyValue;
-
- /**
- * Item connected to this form as datasource.
- */
- private Item itemDatasource;
-
- /**
- * Ordered list of property ids in this editor.
- */
- private final LinkedList<Object> propertyIds = new LinkedList<Object>();
-
- /**
- * Current buffered source exception.
- */
- private Buffered.SourceException currentBufferedSourceException = null;
-
- /**
- * Is the form in buffered mode.
- */
- private boolean buffered = false;
-
- /**
- * Mapping from propertyName to corresponding field.
- */
- private final Map<Object, Field<?>> fields = new HashMap<Object, Field<?>>();
-
- /**
- * Form may act as an Item, its own properties are stored here.
- */
- private final Map<Object, Property<?>> ownProperties = new HashMap<Object, Property<?>>();
-
- /**
- * Field factory for this form.
- */
- private FormFieldFactory fieldFactory;
-
- /**
- * Visible item properties.
- */
- private Collection<?> visibleItemProperties;
-
- /**
- * Form needs to repaint itself if child fields value changes due possible
- * change in form validity.
- *
- * TODO introduce ValidityChangeEvent (#6239) and start using it instead.
- * See e.g. DateField#notifyFormOfValidityChange().
- */
- private final ValueChangeListener fieldValueChangeListener = new ValueChangeListener() {
- @Override
- public void valueChange(Property.ValueChangeEvent event) {
- markAsDirty();
- }
- };
-
- /**
- * If this is true, commit implicitly calls setValidationVisible(true).
- */
- private boolean validationVisibleOnCommit = true;
-
- // special handling for gridlayout; remember initial cursor pos
- private int gridlayoutCursorX = -1;
- private int gridlayoutCursorY = -1;
-
- /**
- * Keeps track of the Actions added to this component, and manages the
- * painting and handling as well. Note that the extended AbstractField is a
- * {@link ShortcutNotifier} and has a actionManager that delegates actions
- * to the containing window. This one does not delegate.
- */
- private ActionManager ownActionManager = new ActionManager(this);
-
- /**
- * Constructs a new form with default layout.
- *
- * <p>
- * By default the form uses {@link FormLayout}.
- * </p>
- */
- public Form() {
- this(null);
- setValidationVisible(false);
- }
-
- /**
- * Constructs a new form with given {@link Layout}.
- *
- * @param formLayout
- * the layout of the form.
- */
- public Form(Layout formLayout) {
- this(formLayout, DefaultFieldFactory.get());
- }
-
- /**
- * Constructs a new form with given {@link Layout} and
- * {@link FormFieldFactory}.
- *
- * @param formLayout
- * the layout of the form.
- * @param fieldFactory
- * the FieldFactory of the form.
- */
- public Form(Layout formLayout, FormFieldFactory fieldFactory) {
- super();
- setLayout(formLayout);
- setFooter(new HorizontalLayout());
- setFormFieldFactory(fieldFactory);
- setValidationVisible(false);
- setWidth(100, UNITS_PERCENTAGE);
- }
-
- @Override
- protected FormState getState() {
- return (FormState) super.getState();
- }
-
- @Override
- protected FormState getState(boolean markAsDirty) {
- return (FormState) super.getState(markAsDirty);
- }
-
- /* Documented in interface */
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- if (ownActionManager != null) {
- ownActionManager.paintActions(null, target);
- }
- }
-
- @Override
- public void changeVariables(Object source, Map<String, Object> variables) {
- // Actions
- if (ownActionManager != null) {
- ownActionManager.handleActions(variables, this);
- }
- }
-
- /**
- * The error message of a Form is the error of the first field with a
- * non-empty error.
- *
- * Empty error messages of the contained fields are skipped, because an
- * empty error indicator would be confusing to the user, especially if there
- * are errors that have something to display. This is also the reason why
- * the calculation of the error message is separate from validation, because
- * validation fails also on empty errors.
- */
- @Override
- public ErrorMessage getErrorMessage() {
-
- // Reimplement the checking of validation error by using
- // getErrorMessage() recursively instead of validate().
- ErrorMessage validationError = null;
- if (isValidationVisible()) {
- for (final Object id : propertyIds) {
- Object f = fields.get(id);
- if (f instanceof AbstractComponent) {
- AbstractComponent field = (AbstractComponent) f;
-
- validationError = field.getErrorMessage();
- if (validationError != null) {
- // Show caption as error for fields with empty errors
- if ("".equals(validationError.toString())) {
- validationError = new UserError(field.getCaption());
- }
- break;
- } else if (f instanceof Field
- && !((Field<?>) f).isValid()) {
- // Something is wrong with the field, but no proper
- // error is given. Generate one.
- validationError = new UserError(field.getCaption());
- break;
- }
- }
- }
- }
-
- // Return if there are no errors at all
- if (getComponentError() == null && validationError == null
- && currentBufferedSourceException == null) {
- return null;
- }
-
- // Throw combination of the error types
- return new CompositeErrorMessage(
- new ErrorMessage[] { getComponentError(), validationError,
- AbstractErrorMessage.getErrorMessageForException(
- currentBufferedSourceException) });
- }
-
- /**
- * Controls the making validation visible implicitly on commit.
- *
- * Having commit() call setValidationVisible(true) implicitly is the default
- * behavior. You can disable the implicit setting by setting this property
- * as false.
- *
- * It is useful, because you usually want to start with the form free of
- * errors and only display them after the user clicks Ok. You can disable
- * the implicit setting by setting this property as false.
- *
- * @param makeVisible
- * If true (default), validation is made visible when commit() is
- * called. If false, the visibility is left as it is.
- */
- public void setValidationVisibleOnCommit(boolean makeVisible) {
- validationVisibleOnCommit = makeVisible;
- }
-
- /**
- * Is validation made automatically visible on commit?
- *
- * See setValidationVisibleOnCommit().
- *
- * @return true if validation is made automatically visible on commit.
- */
- public boolean isValidationVisibleOnCommit() {
- return validationVisibleOnCommit;
- }
-
- /*
- * Commit changes to the data source Don't add a JavaDoc comment here, we
- * use the default one from the interface.
- */
- @Override
- public void commit()
- throws Buffered.SourceException, InvalidValueException {
-
- LinkedList<SourceException> problems = null;
-
- // Only commit on valid state if so requested
- if (!isInvalidCommitted() && !isValid()) {
- /*
- * The values are not ok and we are told not to commit invalid
- * values
- */
- if (validationVisibleOnCommit) {
- setValidationVisible(true);
- }
-
- // Find the first invalid value and throw the exception
- validate();
- }
-
- // Try to commit all
- for (final Object id : propertyIds) {
- try {
- final Field<?> f = fields.get(id);
- // Commit only non-readonly fields.
- if (!f.isReadOnly()) {
- f.commit();
- }
- } catch (final Buffered.SourceException e) {
- if (problems == null) {
- problems = new LinkedList<SourceException>();
- }
- problems.add(e);
- }
- }
-
- // No problems occurred
- if (problems == null) {
- if (currentBufferedSourceException != null) {
- currentBufferedSourceException = null;
- markAsDirty();
- }
- return;
- }
-
- // Commit problems
- final Throwable[] causes = problems
- .toArray(new Throwable[problems.size()]);
-
- final Buffered.SourceException e = new Buffered.SourceException(this,
- causes);
- currentBufferedSourceException = e;
- markAsDirty();
- throw e;
- }
-
- /*
- * Discards local changes and refresh values from the data source Don't add
- * a JavaDoc comment here, we use the default one from the interface.
- */
- @Override
- public void discard() throws Buffered.SourceException {
-
- LinkedList<SourceException> problems = null;
-
- // Try to discard all changes
- for (final Object id : propertyIds) {
- try {
- fields.get(id).discard();
- } catch (final Buffered.SourceException e) {
- if (problems == null) {
- problems = new LinkedList<SourceException>();
- }
- problems.add(e);
- }
- }
-
- // No problems occurred
- if (problems == null) {
- if (currentBufferedSourceException != null) {
- currentBufferedSourceException = null;
- markAsDirty();
- }
- return;
- }
-
- // Discards problems occurred
- final Throwable[] causes = problems
- .toArray(new Throwable[problems.size()]);
-
- final Buffered.SourceException e = new Buffered.SourceException(this,
- causes);
- currentBufferedSourceException = e;
- markAsDirty();
- throw e;
- }
-
- /*
- * Is the object modified but not committed? Don't add a JavaDoc comment
- * here, we use the default one from the interface.
- */
- @Override
- public boolean isModified() {
- for (final Object id : propertyIds) {
- final Field<?> f = fields.get(id);
- if (f != null && f.isModified()) {
- return true;
- }
-
- }
- return false;
- }
-
- /*
- * Sets the editor's buffered mode to the specified status. Don't add a
- * JavaDoc comment here, we use the default one from the interface.
- */
- @Override
- public void setBuffered(boolean buffered) {
- if (buffered != this.buffered) {
- this.buffered = buffered;
- for (final Object id : propertyIds) {
- fields.get(id).setBuffered(buffered);
- }
- }
- }
-
- /**
- * Adds a new property to form and create corresponding field.
- *
- * @see Item#addItemProperty(Object, Property)
- */
- @Override
- public boolean addItemProperty(Object id, Property property) {
-
- // Checks inputs
- if (id == null || property == null) {
- throw new NullPointerException("Id and property must be non-null");
- }
-
- // Checks that the property id is not reserved
- if (propertyIds.contains(id)) {
- return false;
- }
-
- propertyIds.add(id);
- ownProperties.put(id, property);
-
- // Gets suitable field
- final Field<?> field = fieldFactory.createField(this, id, this);
- if (field == null) {
- return false;
- }
-
- // Configures the field
- bindPropertyToField(id, property, field);
-
- // Register and attach the created field
- addField(id, field);
-
- return true;
- }
-
- /**
- * Registers the field with the form and adds the field to the form layout.
- *
- * <p>
- * The property id must not be already used in the form.
- * </p>
- *
- * <p>
- * This field is added to the layout using the
- * {@link #attachField(Object, Field)} method.
- * </p>
- *
- * @param propertyId
- * the Property id the the field.
- * @param field
- * the field which should be added to the form.
- */
- public void addField(Object propertyId, Field<?> field) {
- registerField(propertyId, field);
- attachField(propertyId, field);
- markAsDirty();
- }
-
- /**
- * Register the field with the form. All registered fields are validated
- * when the form is validated and also committed when the form is committed.
- *
- * <p>
- * The property id must not be already used in the form.
- * </p>
- *
- *
- * @param propertyId
- * the Property id of the field.
- * @param field
- * the Field that should be registered
- */
- private void registerField(Object propertyId, Field<?> field) {
- if (propertyId == null || field == null) {
- return;
- }
-
- fields.put(propertyId, field);
- field.addListener(fieldValueChangeListener);
- if (!propertyIds.contains(propertyId)) {
- // adding a field directly
- propertyIds.addLast(propertyId);
- }
-
- // Update the buffered mode and immediate to match the
- // form.
- // Should this also include invalidCommitted (#3993)?
- field.setBuffered(buffered);
- if (isImmediate() && field instanceof AbstractLegacyComponent) {
- ((AbstractLegacyComponent) field).setImmediate(true);
- }
- }
-
- /**
- * Adds the field to the form layout.
- * <p>
- * The field is added to the form layout in the default position (the
- * position used by {@link Layout#addComponent(Component)}. If the
- * underlying layout is a {@link CustomLayout} the field is added to the
- * CustomLayout location given by the string representation of the property
- * id using {@link CustomLayout#addComponent(Component, String)}.
- * </p>
- *
- * <p>
- * Override this method to control how the fields are added to the layout.
- * </p>
- *
- * @param propertyId
- * @param field
- */
- protected void attachField(Object propertyId, Field field) {
- if (propertyId == null || field == null) {
- return;
- }
-
- Layout layout = getLayout();
- if (layout instanceof CustomLayout) {
- ((CustomLayout) layout).addComponent(field, propertyId.toString());
- } else {
- layout.addComponent(field);
- }
-
- }
-
- /**
- * The property identified by the property id.
- *
- * <p>
- * The property data source of the field specified with property id is
- * returned. If there is a (with specified property id) having no data
- * source, the field is returned instead of the data source.
- * </p>
- *
- * @see Item#getItemProperty(Object)
- */
- @Override
- public Property getItemProperty(Object id) {
- final Field<?> field = fields.get(id);
- if (field == null) {
- // field does not exist or it is not (yet) created for this property
- return ownProperties.get(id);
- }
- final Property<?> property = field.getPropertyDataSource();
-
- if (property != null) {
- return property;
- } else {
- return field;
- }
- }
-
- /**
- * Gets the field identified by the propertyid.
- *
- * @param propertyId
- * the id of the property.
- */
- public Field getField(Object propertyId) {
- return fields.get(propertyId);
- }
-
- /* Documented in interface */
- @Override
- public Collection<?> getItemPropertyIds() {
- return Collections.unmodifiableCollection(propertyIds);
- }
-
- /**
- * Removes the property and corresponding field from the form.
- *
- * @see Item#removeItemProperty(Object)
- */
- @Override
- public boolean removeItemProperty(Object id) {
- ownProperties.remove(id);
-
- final Field<?> field = fields.get(id);
-
- if (field != null) {
- propertyIds.remove(id);
- fields.remove(id);
- detachField(field);
- field.removeListener(fieldValueChangeListener);
- return true;
- }
-
- return false;
- }
-
- /**
- * Called when a form field is detached from a Form. Typically when a new
- * Item is assigned to Form via {@link #setItemDataSource(Item)}.
- * <p>
- * Override this method to control how the fields are removed from the
- * layout.
- * </p>
- *
- * @param field
- * the field to be detached from the forms layout.
- */
- protected void detachField(final Field field) {
- Component p = field.getParent();
- if (p instanceof ComponentContainer) {
- ((ComponentContainer) p).removeComponent(field);
- }
- }
-
- /**
- * Removes all properties and fields from the form.
- *
- * @return the Success of the operation. Removal of all fields succeeded if
- * (and only if) the return value is <code>true</code>.
- */
- public boolean removeAllProperties() {
- boolean success = true;
-
- for (Object property : propertyIds.toArray()) {
- if (!removeItemProperty(property)) {
- success = false;
- }
- }
-
- return success;
- }
-
- /* Documented in the interface */
- @Override
- public Item getItemDataSource() {
- return itemDatasource;
- }
-
- /**
- * Sets the item datasource for the form.
- *
- * <p>
- * Setting item datasource clears any fields, the form might contain and
- * adds all the properties as fields to the form.
- * </p>
- *
- * @see Item.Viewer#setItemDataSource(Item)
- */
- @Override
- public void setItemDataSource(Item newDataSource) {
- setItemDataSource(newDataSource,
- newDataSource != null ? newDataSource.getItemPropertyIds()
- : null);
- }
-
- /**
- * Set the item datasource for the form, but limit the form contents to
- * specified properties of the item.
- *
- * <p>
- * Setting item datasource clears any fields, the form might contain and
- * adds the specified the properties as fields to the form, in the specified
- * order.
- * </p>
- *
- * @see Item.Viewer#setItemDataSource(Item)
- */
- public void setItemDataSource(Item newDataSource,
- Collection<?> propertyIds) {
-
- if (getLayout() instanceof GridLayout) {
- GridLayout gl = (GridLayout) getLayout();
- if (gridlayoutCursorX == -1) {
- // first setItemDataSource, remember initial cursor
- gridlayoutCursorX = gl.getCursorX();
- gridlayoutCursorY = gl.getCursorY();
- } else {
- // restore initial cursor
- gl.setCursorX(gridlayoutCursorX);
- gl.setCursorY(gridlayoutCursorY);
- }
- }
-
- // Removes all fields first from the form
- removeAllProperties();
-
- // Sets the datasource
- itemDatasource = newDataSource;
-
- // If the new datasource is null, just set null datasource
- if (itemDatasource == null) {
- markAsDirty();
- return;
- }
-
- // Adds all the properties to this form
- for (final Object id : propertyIds) {
- final Property<?> property = itemDatasource.getItemProperty(id);
- if (id != null && property != null) {
- final Field<?> f = fieldFactory.createField(itemDatasource, id,
- this);
- if (f != null) {
- bindPropertyToField(id, property, f);
- addField(id, f);
- }
- }
- }
- }
-
- /**
- * Binds an item property to a field. The default behavior is to bind
- * property straight to Field. If Property.Viewer type property (e.g.
- * PropertyFormatter) is already set for field, the property is bound to
- * that Property.Viewer.
- *
- * @param propertyId
- * @param property
- * @param field
- * @since 6.7.3
- */
- protected void bindPropertyToField(final Object propertyId,
- final Property property, final Field field) {
- // check if field has a property that is Viewer set. In that case we
- // expect developer has e.g. PropertyFormatter that he wishes to use and
- // assign the property to the Viewer instead.
- boolean hasFilterProperty = field.getPropertyDataSource() != null
- && field.getPropertyDataSource() instanceof Property.Viewer;
- if (hasFilterProperty) {
- ((Property.Viewer) field.getPropertyDataSource())
- .setPropertyDataSource(property);
- } else {
- field.setPropertyDataSource(property);
- }
- }
-
- /**
- * Gets the layout of the form.
- *
- * <p>
- * By default form uses <code>OrderedLayout</code> with <code>form</code>
- * -style.
- * </p>
- *
- * @return the Layout of the form.
- */
- public Layout getLayout() {
- return (Layout) getState(false).layout;
- }
-
- /**
- * Sets the layout of the form.
- *
- * <p>
- * If set to null then Form uses a FormLayout by default.
- * </p>
- *
- * @param layout
- * the layout of the form.
- */
- public void setLayout(Layout layout) {
-
- // Use orderedlayout by default
- if (layout == null) {
- layout = new FormLayout();
- }
-
- // reset cursor memory
- gridlayoutCursorX = -1;
- gridlayoutCursorY = -1;
-
- // Move fields from previous layout
- if (getLayout() != null) {
- for (Object property : propertyIds) {
- Field<?> f = getField(property);
- detachField(f);
- if (layout instanceof CustomLayout) {
- ((CustomLayout) layout).addComponent(f,
- property.toString());
- } else {
- layout.addComponent(f);
- }
- }
-
- getLayout().setParent(null);
- }
-
- // Replace the previous layout
- layout.setParent(this);
- getState().layout = layout;
- }
-
- /**
- * Sets the form field to be selectable from static list of changes.
- *
- * <p>
- * The list values and descriptions are given as array. The value-array must
- * contain the current value of the field and the lengths of the arrays must
- * match. Null values are not supported.
- * </p>
- *
- * Note: since Vaadin 7.0, returns an {@link AbstractSelect} instead of a
- * {@link Select}.
- *
- * @param propertyId
- * the id of the property.
- * @param values
- * @param descriptions
- * @return the select property generated
- */
- public AbstractSelect replaceWithSelect(Object propertyId, Object[] values,
- Object[] descriptions) {
-
- // Checks the parameters
- if (propertyId == null || values == null || descriptions == null) {
- throw new NullPointerException("All parameters must be non-null");
- }
- if (values.length != descriptions.length) {
- throw new IllegalArgumentException(
- "Value and description list are of different size");
- }
-
- // Gets the old field
- final Field<?> oldField = fields.get(propertyId);
- if (oldField == null) {
- throw new IllegalArgumentException("Field with given propertyid '"
- + propertyId + "' can not be found.");
- }
- final Object value = oldField.getPropertyDataSource() == null
- ? oldField.getValue()
- : oldField.getPropertyDataSource().getValue();
-
- // Checks that the value exists and check if the select should
- // be forced in multiselect mode
- boolean found = false;
- boolean isMultiselect = false;
- for (int i = 0; i < values.length && !found; i++) {
- if (values[i] == value
- || value != null && value.equals(values[i])) {
- found = true;
- }
- }
- if (value != null && !found) {
- if (value instanceof Collection) {
- for (final Object val : ((Collection<?>) value)) {
- found = false;
- for (int i = 0; i < values.length && !found; i++) {
- if (values[i] == val
- || val != null && val.equals(values[i])) {
- found = true;
- }
- }
- if (!found) {
- throw new IllegalArgumentException(
- "Currently selected value '" + val
- + "' of property '" + propertyId
- + "' was not found");
- }
- }
- isMultiselect = true;
- } else {
- throw new IllegalArgumentException("Current value '" + value
- + "' of property '" + propertyId + "' was not found");
- }
- }
-
- // Creates the new field matching to old field parameters
- final AbstractSelect newField = isMultiselect ? new ListSelect()
- : new Select();
- newField.setCaption(oldField.getCaption());
- newField.setReadOnly(oldField.isReadOnly());
- newField.setBuffered(oldField.isBuffered());
-
- // Creates the options list
- newField.addContainerProperty("desc", String.class, "");
- newField.setItemCaptionPropertyId("desc");
- for (int i = 0; i < values.length; i++) {
- Object id = values[i];
- final Item item;
- if (id == null) {
- id = newField.addItem();
- item = newField.getItem(id);
- newField.setNullSelectionItemId(id);
- } else {
- item = newField.addItem(id);
- }
-
- if (item != null) {
- item.getItemProperty("desc")
- .setValue(descriptions[i].toString());
- }
- }
-
- // Sets the property data source
- final Property<?> property = oldField.getPropertyDataSource();
- oldField.setPropertyDataSource(null);
- newField.setPropertyDataSource(property);
-
- // Replaces the old field with new one
- getLayout().replaceComponent(oldField, newField);
- fields.put(propertyId, newField);
- newField.addListener(fieldValueChangeListener);
- oldField.removeListener(fieldValueChangeListener);
-
- return newField;
- }
-
- /**
- * Checks the validity of the Form and all of its fields.
- *
- * @see Validatable#validate()
- */
- @Override
- public void validate() throws InvalidValueException {
- super.validate();
- for (final Object id : propertyIds) {
- fields.get(id).validate();
- }
- }
-
- /**
- * Checks the validabtable object accept invalid values.
- *
- * @see Validatable#isInvalidAllowed()
- */
- @Override
- public boolean isInvalidAllowed() {
- return true;
- }
-
- /**
- * Should the validabtable object accept invalid values.
- *
- * @see Validatable#setInvalidAllowed(boolean)
- */
- @Override
- public void setInvalidAllowed(boolean invalidValueAllowed)
- throws UnsupportedOperationException {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Sets the component's to read-only mode to the specified state.
- *
- * @see Component#setReadOnly(boolean)
- */
- @Override
- public void setReadOnly(boolean readOnly) {
- super.setReadOnly(readOnly);
- for (final Object id : propertyIds) {
- fields.get(id).setReadOnly(readOnly);
- }
- }
-
- /**
- * Sets the field factory used by this Form to genarate Fields for
- * properties.
- *
- * {@link FormFieldFactory} is used to create fields for form properties.
- * {@link DefaultFieldFactory} is used by default.
- *
- * @param fieldFactory
- * the new factory used to create the fields.
- * @see Field
- * @see FormFieldFactory
- */
- public void setFormFieldFactory(FormFieldFactory fieldFactory) {
- this.fieldFactory = fieldFactory;
- }
-
- /**
- * Get the field factory of the form.
- *
- * @return the FormFieldFactory Factory used to create the fields.
- */
- public FormFieldFactory getFormFieldFactory() {
- return fieldFactory;
- }
-
- /**
- * Gets the field type.
- *
- * @see AbstractField#getType()
- */
- @Override
- public Class<?> getType() {
- if (getPropertyDataSource() != null) {
- return getPropertyDataSource().getType();
- }
- return Object.class;
- }
-
- /**
- * Sets the internal value.
- *
- * This is relevant when the Form is used as Field.
- *
- * @see AbstractField#setInternalValue(java.lang.Object)
- */
- @Override
- protected void setInternalValue(Object newValue) {
- // Stores the old value
- final Object oldValue = propertyValue;
-
- // Sets the current Value
- super.setInternalValue(newValue);
- propertyValue = newValue;
-
- // Ignores form updating if data object has not changed.
- if (oldValue != newValue) {
- setFormDataSource(newValue, getVisibleItemProperties());
- }
- }
-
- /**
- * Gets the first focusable field in form. If there are enabled,
- * non-read-only fields, the first one of them is returned. Otherwise, the
- * field for the first property (or null if none) is returned.
- *
- * @return the Field.
- */
- private Field<?> getFirstFocusableField() {
- Collection<?> itemPropertyIds = getItemPropertyIds();
- if (itemPropertyIds != null && !itemPropertyIds.isEmpty()) {
- for (Object id : itemPropertyIds) {
- if (id != null) {
- Field<?> field = getField(id);
- if (field.isConnectorEnabled() && !field.isReadOnly()) {
- return field;
- }
- }
- }
- // fallback: first field if none of the fields is enabled and
- // writable
- Object id = itemPropertyIds.iterator().next();
- if (id != null) {
- return getField(id);
- }
- }
- return null;
- }
-
- /**
- * Updates the internal form datasource.
- *
- * Method setFormDataSource.
- *
- * @param data
- * @param properties
- */
- protected void setFormDataSource(Object data, Collection<?> properties) {
-
- // If data is an item use it.
- Item item = null;
- if (data instanceof Item) {
- item = (Item) data;
- } else if (data != null) {
- item = new BeanItem<Object>(data);
- }
-
- // Sets the datasource to form
- if (item != null && properties != null) {
- // Shows only given properties
- this.setItemDataSource(item, properties);
- } else {
- // Shows all properties
- this.setItemDataSource(item);
- }
- }
-
- /**
- * Returns the visibleProperties.
- *
- * @return the Collection of visible Item properites.
- */
- public Collection<?> getVisibleItemProperties() {
- return visibleItemProperties;
- }
-
- /**
- * Sets the visibleProperties.
- *
- * @param visibleProperties
- * the visibleProperties to set.
- */
- public void setVisibleItemProperties(Collection<?> visibleProperties) {
- visibleItemProperties = visibleProperties;
- Object value = getValue();
- if (value == null) {
- value = itemDatasource;
- }
- setFormDataSource(value, getVisibleItemProperties());
- }
-
- /**
- * Sets the visibleProperties.
- *
- * @param visibleProperties
- * the visibleProperties to set.
- */
- public void setVisibleItemProperties(Object... visibleProperties) {
- LinkedList<Object> v = new LinkedList<Object>();
- for (int i = 0; i < visibleProperties.length; i++) {
- v.add(visibleProperties[i]);
- }
- setVisibleItemProperties(v);
- }
-
- /**
- * Focuses the first field in the form.
- *
- * @see Component.Focusable#focus()
- */
- @Override
- public void focus() {
- final Field<?> f = getFirstFocusableField();
- if (f != null) {
- f.focus();
- }
- }
-
- /**
- * Sets the Tabulator index of this Focusable component.
- *
- * @see Component.Focusable#setTabIndex(int)
- */
- @Override
- public void setTabIndex(int tabIndex) {
- super.setTabIndex(tabIndex);
- for (final Object id : getItemPropertyIds()) {
- getField(id).setTabIndex(tabIndex);
- }
- }
-
- /**
- * Setting the form to be immediate also sets all the fields of the form to
- * the same state.
- */
- @Override
- public void setImmediate(boolean immediate) {
- super.setImmediate(immediate);
- for (Field<?> f : fields.values()) {
- if (f instanceof AbstractLegacyComponent) {
- ((AbstractLegacyComponent) f).setImmediate(immediate);
- }
- }
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * A Form is empty if all of its fields are empty.
- *
- */
- @Override
- public boolean isEmpty() {
- for (Field<?> f : fields.values()) {
- if (f instanceof AbstractField) {
- if (!((AbstractField<?>) f).isEmpty()) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- @Override
- public void clear() {
- for (Field<?> f : fields.values()) {
- if (f instanceof AbstractField) {
- ((AbstractField<?>) f).clear();
- }
- }
- }
-
- /**
- * Adding validators directly to form is not supported.
- *
- * Add the validators to form fields instead.
- */
- @Override
- public void addValidator(Validator validator) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Returns a layout that is rendered below normal form contents. This area
- * can be used for example to include buttons related to form contents.
- *
- * @return layout rendered below normal form contents or null if no footer
- * is used
- */
- public Layout getFooter() {
- return (Layout) getState(false).footer;
- }
-
- /**
- * Sets the layout that is rendered below normal form contents. No footer is
- * rendered if this is set to null, .
- *
- * @param footer
- * the new footer layout
- */
- public void setFooter(Layout footer) {
- if (getFooter() != null) {
- getFooter().setParent(null);
- }
- getState().footer = footer;
- if (footer != null) {
- footer.setParent(this);
- }
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- if (getParent() != null && !getParent().isEnabled()) {
- // some ancestor still disabled, don't update children
- return;
- } else {
- getLayout().markAsDirtyRecursive();
- }
- }
-
- /*
- * ACTIONS
- */
-
- /**
- * Gets the {@link ActionManager} responsible for handling {@link Action}s
- * added to this Form.<br/>
- * Note that Form has another ActionManager inherited from
- * {@link AbstractField}. The ownActionManager handles Actions attached to
- * this Form specifically, while the ActionManager in AbstractField
- * delegates to the containing Window (i.e global Actions).
- *
- * @return
- */
- protected ActionManager getOwnActionManager() {
- if (ownActionManager == null) {
- ownActionManager = new ActionManager(this);
- }
- return ownActionManager;
- }
-
- @Override
- public void addActionHandler(Handler actionHandler) {
- getOwnActionManager().addActionHandler(actionHandler);
- }
-
- @Override
- public void removeActionHandler(Handler actionHandler) {
- if (ownActionManager != null) {
- ownActionManager.removeActionHandler(actionHandler);
- }
- }
-
- /**
- * Removes all action handlers.
- */
- public void removeAllActionHandlers() {
- if (ownActionManager != null) {
- ownActionManager.removeAllActionHandlers();
- }
- }
-
- @Override
- public <T extends Action & com.vaadin.event.Action.Listener> void addAction(
- T action) {
- getOwnActionManager().addAction(action);
- }
-
- @Override
- public <T extends Action & com.vaadin.event.Action.Listener> void removeAction(
- T action) {
- if (ownActionManager != null) {
- ownActionManager.removeAction(action);
- }
- }
-
- @Override
- public Iterator<Component> iterator() {
- return new ComponentIterator();
- }
-
- /**
- * Modifiable and Serializable Iterator for the components, used by
- * {@link Form#getComponentIterator()}.
- */
- private class ComponentIterator
- implements Iterator<Component>, Serializable {
-
- int i = 0;
-
- @Override
- public boolean hasNext() {
- if (i < getComponentCount()) {
- return true;
- }
- return false;
- }
-
- @Override
- public Component next() {
- if (!hasNext()) {
- return null;
- }
- i++;
- if (i == 1) {
- if (getLayout() != null) {
- return getLayout();
- }
- if (getFooter() != null) {
- return getFooter();
- }
- } else if (i == 2) {
- if (getFooter() != null) {
- return getFooter();
- }
- }
- return null;
- }
-
- @Override
- public void remove() {
- if (i == 1) {
- if (getLayout() != null) {
- setLayout(null);
- i = 0;
- } else {
- setFooter(null);
- }
- } else if (i == 2) {
- setFooter(null);
- }
- }
- }
-
- /**
- * @deprecated As of 7.0, use {@link #iterator()} instead.
- */
- @Deprecated
- public Iterator<Component> getComponentIterator() {
- return iterator();
- }
-
- public int getComponentCount() {
- int count = 0;
- if (getLayout() != null) {
- count++;
- }
- if (getFooter() != null) {
- count++;
- }
-
- return count;
- }
-
- }
|