    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
    015    package org.apache.tapestry5.corelib.components;
    016
    017    import org.apache.tapestry5.*;
    018    import org.apache.tapestry5.annotations.*;
    019    import org.apache.tapestry5.beaneditor.BeanModel;
    020    import org.apache.tapestry5.internal.beaneditor.BeanModelUtils;
    021    import org.apache.tapestry5.ioc.annotations.Inject;
    022    import 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)
    051    public 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    }