001// Licensed under the Apache License, Version 2.0 (the "License");
002// you may not use this file except in compliance with the License.
003// You may obtain a copy of the License at
004//
005// http://www.apache.org/licenses/LICENSE-2.0
006//
007// Unless required by applicable law or agreed to in writing, software
008// distributed under the License is distributed on an "AS IS" BASIS,
009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010// See the License for the specific language governing permissions and
011// limitations under the License.
012
013package org.apache.tapestry5.corelib.components;
014
015import org.apache.tapestry5.*;
016import org.apache.tapestry5.annotations.*;
017import org.apache.tapestry5.beanmodel.BeanModel;
018import org.apache.tapestry5.beanmodel.BeanModelUtils;
019import org.apache.tapestry5.beanmodel.services.BeanModelSource;
020import org.apache.tapestry5.ioc.annotations.Inject;
021
022/**
023 * A component that creates an entire form for editing the properties of a particular bean (or POJO, or any object
024 * with properties). Generates a simple UI for editing the properties of the object, with the UI for each
025 * property (text field, checkbox, drop down list) determined from the property type (or by other means, such as an
026 * annotation), and the order and validation for the properties determined from annotations on the property's getter and
027 * setter methods.
028 * <p>
029 * You may add block parameters to the component; when the name matches the name of a property (case insensitively), then
030 * the corresponding Block is rendered, rather than any of the built in property editor blocks. This allows you to
031 * override specific properties with your own customized UI, for cases where the default UI is insufficient, or no
032 * built-in editor type is appropriate.
033 * <p>
034 * BeanEditForm contains a {@link org.apache.tapestry5.corelib.components.Form} component and will trigger all the
035 * events of a Form.
036 *
037 * @tapestrydoc
038 * @see org.apache.tapestry5.beanmodel.BeanModel
039 * @see org.apache.tapestry5.beanmodel.services.BeanModelSource
040 * @see org.apache.tapestry5.corelib.components.PropertyEditor
041 * @see org.apache.tapestry5.beaneditor.DataType
042 * @see Form
043 * @see Errors
044 * @see BeanEditor
045 */
046@SupportsInformalParameters
047@Events(EventConstants.PREPARE)
048public class BeanEditForm implements ClientElement, FormValidationControl
049{
050
051    /**
052     * The text label for the submit button of the form, by default "Create/Update".
053     */
054    @Parameter(value = "message:core-submit-label", defaultPrefix = BindingConstants.LITERAL)
055    @Property
056    private String submitLabel;
057
058    /**
059     * The object to be edited. This will be read when the component renders and updated when the form for the component
060     * is submitted. Typically, the container will listen for a "prepare" event, in order to ensure that a non-null
061     * value is ready to be read or updated. Often, the BeanEditForm can create the object as needed (assuming a public,
062     * no arguments constructor). The object property defaults to a property with the same name as the component id.
063     */
064    @Parameter(required = true, autoconnect = true)
065    @Property
066    private Object object;
067
068    /**
069     * A comma-separated list of property names to be retained from the
070     * {@link org.apache.tapestry5.beanmodel.BeanModel} (only used
071     * when a default model is created automatically).
072     * Only these properties will be retained, and the properties will also be reordered. The names are
073     * case-insensitive.
074     */
075    @Parameter(defaultPrefix = BindingConstants.LITERAL)
076    private String include;
077
078    /**
079     * A comma-separated list of property names to be removed from the {@link org.apache.tapestry5.beanmodel.BeanModel}
080     * (only used
081     * when a default model is created automatically).
082     * The names are case-insensitive.
083     */
084    @Parameter(defaultPrefix = BindingConstants.LITERAL)
085    private String exclude;
086
087    /**
088     * A comma-separated list of property names indicating the order in which the properties should be presented. The
089     * names are case insensitive. Any properties not indicated in the list will be appended to the end of the display
090     * orde. Only used
091     * when a default model is created automatically.
092     */
093    @Parameter(defaultPrefix = BindingConstants.LITERAL)
094    private String reorder;
095
096    /**
097     * A comma-separated list of property names to be added to the {@link org.apache.tapestry5.beanmodel.BeanModel}
098     * (only used
099     * when a default model is created automatically).
100     */
101    @Parameter(defaultPrefix = BindingConstants.LITERAL)
102    private String add;
103
104    /**
105     * Specifies the CSS class attribute for the form; the factory default is "well".
106     */
107    @Property
108    @Parameter(name = "class", defaultPrefix = BindingConstants.LITERAL, value = "message:private-core-components.beaneditform.class")
109    private String className;
110
111    @Component(parameters = "validationId=componentResources.id", publishParameters = "clientValidation,autofocus,zone")
112    private Form form;
113
114    /**
115     * If set to true, then the form will include an additional button after the submit button labeled "Cancel".
116     * The cancel button will submit the form, bypassing client-side validation. The BeanEditForm will fire a
117     * {@link EventConstants#CANCELED} event (before the form's {@link EventConstants#VALIDATE} event).
118     *
119     * @since 5.2.0
120     */
121    @Property
122    @Parameter
123    private boolean cancel;
124
125    /**
126     * The model that identifies the parameters to be edited, their order, and every other aspect. If not specified, a
127     * default bean model will be created from the type of the object bound to the object parameter. The add, include,
128     * exclude and reorder parameters are <em>only</em> applied to a default model, not an explicitly provided one.
129     */
130    @SuppressWarnings("unused")
131    @Parameter
132    @Property
133    private BeanModel model;
134
135    @Inject
136    private ComponentResources resources;
137
138    @Inject
139    private BeanModelSource beanModelSource;
140
141    boolean onPrepareFromForm()
142    {
143        resources.triggerEvent(EventConstants.PREPARE, null, null);
144
145        if (model == null)
146        {
147            Class beanType = resources.getBoundType("object");
148
149            model = beanModelSource.createEditModel(beanType, resources.getContainerMessages());
150
151            BeanModelUtils.modify(model, add, include, exclude, reorder);
152        }
153
154        return true;
155    }
156
157    /**
158     * Returns the client id of the embedded form.
159     */
160    public String getClientId()
161    {
162        return form.getClientId();
163    }
164
165    public void clearErrors()
166    {
167        form.clearErrors();
168    }
169
170    public boolean getHasErrors()
171    {
172        return form.getHasErrors();
173    }
174
175    public boolean isValid()
176    {
177        return form.isValid();
178    }
179
180    public void recordError(Field field, String errorMessage)
181    {
182        form.recordError(field, errorMessage);
183    }
184
185    public void recordError(String errorMessage)
186    {
187        form.recordError(errorMessage);
188    }
189}