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