001    // Copyright 2007, 2008, 2009 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     * @see org.apache.tapestry5.beaneditor.BeanModel
041     * @see org.apache.tapestry5.services.BeanModelSource
042     * @see org.apache.tapestry5.corelib.components.PropertyEditor
043     * @see org.apache.tapestry5.beaneditor.DataType
044     */
045    @SupportsInformalParameters
046    @Events(EventConstants.PREPARE)
047    public class BeanEditForm implements ClientElement, FormValidationControl
048    {
049    
050        /**
051         * The text label for the submit button of the form, by default "Create/Update".
052         */
053        @Parameter(value = "message:submit-label", defaultPrefix = BindingConstants.LITERAL)
054        @Property
055        private String submitLabel;
056    
057        /**
058         * The object to be edited. This will be read when the component renders and updated when the form for the component
059         * is submitted. Typically, the container will listen for a "prepare" event, in order to ensure that a non-null
060         * value is ready to be read or updated. Often, the BeanEditForm can create the object as needed (assuming a public,
061         * no arguments constructor).  The object property defaults to a property with the same name as the component id.
062         */
063        @Parameter(required = true, autoconnect = true)
064        @Property
065        private Object object;
066    
067        /**
068         * A comma-separated list of property names to be retained from the {@link org.apache.tapestry5.beaneditor.BeanModel}.
069         * Only these properties will be retained, and the properties will also be reordered. The names are
070         * case-insensitive.
071         */
072        @SuppressWarnings("unused")
073        @Parameter(defaultPrefix = BindingConstants.LITERAL)
074        private String include;
075    
076        /**
077         * A comma-separated list of property names to be added to the {@link org.apache.tapestry5.beaneditor.BeanModel}.
078         */
079        @Parameter(defaultPrefix = BindingConstants.LITERAL)
080        private String add;
081    
082        /**
083         * A comma-separated list of property names to be removed from the {@link org.apache.tapestry5.beaneditor.BeanModel}.
084         * The names are case-insensitive.
085         */
086        @SuppressWarnings("unused")
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         * order.
094         */
095        @SuppressWarnings("unused")
096        @Parameter(defaultPrefix = BindingConstants.LITERAL)
097        private String reorder;
098    
099        @Component(parameters =
100                "validationId=componentResources.id", publishParameters = "clientValidation,autofocus,zone")
101        private Form form;
102    
103        /**
104         * The model that identifies the parameters to be edited, their order, and every other aspect. If not specified, a
105         * default bean model will be created from the type of the object bound to the object parameter.
106         */
107        @SuppressWarnings("unused")
108        @Parameter
109        @Property
110        private BeanModel model;
111    
112        @Inject
113        private ComponentResources resources;
114    
115        @Inject
116        private BeanModelSource beanModelSource;
117    
118    
119        void onPrepareFromForm()
120        {
121            resources.triggerEvent(EventConstants.PREPARE, null, null);
122    
123            if (model == null)
124            {
125                Class beanType = resources.getBoundType("object");
126    
127                model = beanModelSource.createEditModel(beanType, resources.getContainerMessages());
128            }
129    
130            BeanModelUtils.modify(model, add, include, exclude, reorder);
131        }
132    
133    
134        /**
135         * Returns the client id of the embedded form.
136         */
137        public String getClientId()
138        {
139            return form.getClientId();
140        }
141    
142        public void clearErrors()
143        {
144            form.clearErrors();
145        }
146    
147        public boolean getHasErrors()
148        {
149            return form.getHasErrors();
150        }
151    
152        public boolean isValid()
153        {
154            return form.isValid();
155        }
156    
157        public void recordError(Field field, String errorMessage)
158        {
159            form.recordError(field, errorMessage);
160        }
161    
162        public void recordError(String errorMessage)
163        {
164            form.recordError(errorMessage);
165        }
166    }