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