001// Copyright 2007, 2008, 2009, 2010, 2011 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: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    @Component(parameters = "validationId=componentResources.id", publishParameters = "clientValidation,autofocus,zone")
108    private Form form;
109
110    /**
111     * If set to true, then the form will include an additional button after the submit button labeled "Cancel".
112     * The cancel button will submit the form, bypassing client-side validation. The BeanEditForm will fire a
113     * {@link EventConstants#CANCELED} event (before the form's {@link EventConstants#VALIDATE} event).
114     *
115     * @since 5.2.0
116     */
117    @Property
118    @Parameter
119    private boolean cancel;
120
121    /**
122     * The model that identifies the parameters to be edited, their order, and every other aspect. If not specified, a
123     * default bean model will be created from the type of the object bound to the object parameter. The add, include,
124     * exclude and reorder parameters are <em>only</em> applied to a default model, not an explicitly provided one.
125     */
126    @SuppressWarnings("unused")
127    @Parameter
128    @Property
129    private BeanModel model;
130
131    @Inject
132    private ComponentResources resources;
133
134    @Inject
135    private BeanModelSource beanModelSource;
136
137    boolean onPrepareFromForm()
138    {
139        resources.triggerEvent(EventConstants.PREPARE, null, null);
140
141        if (model == null)
142        {
143            Class beanType = resources.getBoundType("object");
144
145            model = beanModelSource.createEditModel(beanType, resources.getContainerMessages());
146
147            BeanModelUtils.modify(model, add, include, exclude, reorder);
148        }
149
150        return true;
151    }
152
153    /**
154     * Returns the client id of the embedded form.
155     */
156    public String getClientId()
157    {
158        return form.getClientId();
159    }
160
161    public void clearErrors()
162    {
163        form.clearErrors();
164    }
165
166    public boolean getHasErrors()
167    {
168        return form.getHasErrors();
169    }
170
171    public boolean isValid()
172    {
173        return form.isValid();
174    }
175
176    public void recordError(Field field, String errorMessage)
177    {
178        form.recordError(field, errorMessage);
179    }
180
181    public void recordError(String errorMessage)
182    {
183        form.recordError(errorMessage);
184    }
185}