001    /*
002     * Copyright 2002-2004 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.form.builder;
017    
018    import java.util.HashMap;
019    import java.util.Map;
020    
021    import javax.swing.JComboBox;
022    import javax.swing.JComponent;
023    import javax.swing.JLabel;
024    import javax.swing.JScrollPane;
025    
026    import org.springframework.rules.constraint.Constraint;
027    import org.springframework.richclient.form.binding.Binding;
028    import org.springframework.richclient.form.binding.BindingFactory;
029    import org.springframework.richclient.form.binding.swing.ComboBoxBinder;
030    import org.springframework.richclient.layout.TableLayoutBuilder;
031    import org.springframework.util.Assert;
032    
033    /**
034     * A TableFormBuilder builds a form by using a {@link TableLayoutBuilder}
035     * 
036     * @author oliverh
037     * @author Mathias Broekelmann
038     */
039    public class TableFormBuilder extends AbstractFormBuilder {
040    
041        private static final String VALIGN_TOP = TableLayoutBuilder.VALIGN + "=top";
042    
043        private TableLayoutBuilder builder;
044    
045        private String labelAttributes = TableLayoutBuilder.DEFAULT_LABEL_ATTRIBUTES;
046    
047        /**
048         * Creates an instances of the TableFormBuilder by using a {@link BindingFactory}
049         * 
050         * @param bindingFactory
051         *            the binding factory to use to create field bindings.
052         */
053        public TableFormBuilder(BindingFactory bindingFactory) {
054            this(bindingFactory, null);
055        }
056    
057        /**
058         * Creates an instances of the TableFormBuilder by using a {@link BindingFactory} and a given {@ TableLayoutBuilder}
059         * 
060         * @param bindingFactory
061         *            the binding factory to use to create field bindings.
062         */
063        public TableFormBuilder(BindingFactory bindingFactory, TableLayoutBuilder tableLayoutBuilder) {
064            super(bindingFactory);
065            this.builder = tableLayoutBuilder;
066        }
067    
068        /**
069         * adds a row to the form. All subsequent field additions will be placed in a new row
070         */
071        public void row() {
072            getLayoutBuilder().relatedGapRow();
073        }
074    
075        /**
076         * Adds the field to the form. {@link #createDefaultBinding(String)} is used to create the binding for the field
077         * 
078         * @param fieldName
079         *            the name of the field to add
080         * @param attributes
081         *            optional layout attributes for the component. See {@link TableLayoutBuilder} for syntax details
082         * @return an array containing the label and the component which where added to the form
083         */
084        public JComponent[] add(String fieldName) {
085            return add(fieldName, "");
086        }
087    
088        /**
089         * Adds the field binding to the form.
090         * 
091         * @param binding
092         *            the field binding to add
093         * @return an array containing the label and the component of the binding which where added to the form
094         */
095        public JComponent[] add(Binding binding) {
096            return add(binding, "");
097        }
098    
099        /**
100         * Adds the field to the form. {@link #createDefaultBinding(String)} is used to create the binding for the field
101         * 
102         * @param fieldName
103         *            the name of the field to add
104         * @param attributes
105         *            optional layout attributes for the component. See {@link TableLayoutBuilder} for syntax details
106         * @return an array containing the label and the component which where added to the form
107         */
108        public JComponent[] add(String fieldName, String attributes) {
109            return addBinding(createDefaultBinding(fieldName), attributes, getLabelAttributes());
110        }
111    
112        /**
113         * Adds the field binding to the form.
114         * 
115         * @param binding
116         *            the field binding to add
117         * @param attributes
118         *            optional layout attributes for the component. See {@link TableLayoutBuilder} for syntax details
119         * @return an array containing the label and the component which where added to the form
120         */
121        public JComponent[] add(Binding binding, String attributes) {
122            return addBinding(binding, attributes, getLabelAttributes());
123        }
124    
125        /**
126         * Adds the field to the form by using the provided component.
127         * 
128         * @param fieldName
129         *            the name of the field to add
130         * @param component
131         *            the component for the field
132         * @return an array containing the label and the component which where added to the form
133         */
134        public JComponent[] add(String fieldName, JComponent component) {
135            return add(fieldName, component, "");
136        }
137    
138        /**
139         * Adds the field to the form by using the provided component. {@link #createBinding(String, JComponent)} is used to
140         * create the binding of the field
141         * 
142         * @param fieldName
143         *            the name of the field to add
144         * @param component
145         *            the component for the field
146         * @param attributes
147         *            optional layout attributes for the component. See {@link TableLayoutBuilder} for syntax details
148         * @return an array containing the label and the component which where added to the form
149         */
150        public JComponent[] add(String fieldName, JComponent component, String attributes) {
151            return addBinding(createBinding(fieldName, component), attributes, getLabelAttributes());
152        }
153    
154        /**
155         * Adds the field to the form by using a selector component. {@link #createSelector(String, Constraint)} is used to
156         * create the component for the selector
157         * 
158         * @param fieldName
159         *            the name of the field to add
160         * @param filter
161         *            optional filter constraint for the items of the selector
162         * @return an array containing the label and the selector component which where added to the form
163         * 
164         * @see #createSelector(String, org.springframework.rules.constraint.Constraint)
165         */
166        public JComponent[] addSelector(String fieldName, Constraint filter) {
167            return addSelector(fieldName, filter, "");
168        }
169    
170        /**
171         * Adds the field to the form by using a selector component.
172         * 
173         * @param fieldName
174         *            the name of the field to add
175         * @param filter
176         *            optional filter constraint for the items of the selector
177         * @param attributes
178         *            optional layout attributes for the selector component. See {@link TableLayoutBuilder} for syntax
179         *            details
180         * @return an array containing the label and the selector component which where added to the form
181         * 
182         */
183        public JComponent[] addSelector(String fieldName, Constraint filter, String attributes) {
184            Map context = new HashMap();
185            context.put(ComboBoxBinder.FILTER_KEY, filter);
186            return addBinding(getBindingFactory().createBinding(JComboBox.class, fieldName), attributes,
187                    getLabelAttributes());
188        }
189    
190        /**
191         * Adds the field to the form by using a password component. {@link #createPasswordField(String)} is used to create
192         * the component for the password field
193         * 
194         * @param fieldName
195         *            the name of the field to add
196         * @return an array containing the label and the password component which where added to the form
197         * 
198         * @see #createPasswordField(String)
199         */
200        public JComponent[] addPasswordField(String fieldName) {
201            return addPasswordField(fieldName, "");
202        }
203    
204        /**
205         * Adds the field to the form by using a password component. {@link #createPasswordField(String)} is used to create
206         * the component for the password field
207         * 
208         * @param fieldName
209         *            the name of the field to add
210         * @param attributes
211         *            optional layout attributes for the password component. See {@link TableLayoutBuilder} for syntax
212         *            details
213         * @return an array containing the label and the password component which where added to the form
214         * 
215         * @see #createPasswordField(String)
216         */
217        public JComponent[] addPasswordField(String fieldName, String attributes) {
218            return addBinding(createBinding(fieldName, createPasswordField(fieldName)), attributes, getLabelAttributes());
219        }
220    
221        /**
222         * Adds the field to the form by using a text area component which is wrapped inside a scrollpane.
223         * <p>
224         * Note: this method ensures that the the label of the textarea has a top vertical alignment if <code>valign</code>
225         * is not defined in the default label attributes
226         * 
227         * @param fieldName
228         *            the name of the field to add
229         * @return an array containing the label, the textarea and the scrollpane which where added to the form
230         * 
231         * @see #createTextArea(String)
232         */
233        public JComponent[] addTextArea(String fieldName) {
234            return addTextArea(fieldName, "");
235        }
236    
237        /**
238         * Adds the field to the form by using a text area component which is wrapped inside a scrollpane.
239         * {@link #createTextArea(String)} is used to create the component for the text area field
240         * <p>
241         * Note: this method ensures that the the label of the textarea has a top vertical alignment if <code>valign</code>
242         * is not defined in the default label attributes
243         * 
244         * @param fieldName
245         *            the name of the field to add
246         * @param attributes
247         *            optional layout attributes for the scrollpane. See {@link TableLayoutBuilder} for syntax details
248         * @return an array containing the label, the textarea and the scrollpane and which where added to the form
249         * 
250         * @see #createTextArea(String)
251         */
252        public JComponent[] addTextArea(String fieldName, String attributes) {
253            JComponent textArea = createTextArea(fieldName);
254            String labelAttributes = getLabelAttributes();
255            if (labelAttributes == null) {
256                labelAttributes = VALIGN_TOP;
257            } else if (!labelAttributes.contains(TableLayoutBuilder.VALIGN)) {
258                labelAttributes += " " + VALIGN_TOP;
259            }
260            return addBinding(createBinding(fieldName, textArea), new JScrollPane(textArea), attributes, labelAttributes);
261        }
262    
263        /**
264         * Adds the field to the form by using the default binding. The component will be placed inside a scrollpane.
265         * 
266         * @param fieldName
267         *            the name of the field to add
268         * @return an array containing the label, the component of the field binding and the scrollpane which where added to
269         *         the form
270         */
271        public JComponent[] addInScrollPane(String fieldName) {
272            return addInScrollPane(fieldName, "");
273        }
274    
275        /**
276         * Adds the field to the form by using the default binding. The component will be placed inside a scrollpane.
277         * 
278         * @param fieldName
279         *            the name of the field to add
280         * @param attributes
281         *            optional layout attributes for the scrollpane. See {@link TableLayoutBuilder} for syntax details
282         * @return an array containing the label, the component of the field binding and the scrollpane binding which where
283         *         added to the form
284         * 
285         * @see #createScrollPane(String, JComponent)
286         */
287        public JComponent[] addInScrollPane(String fieldName, String attributes) {
288            return addInScrollPane(createDefaultBinding(fieldName), attributes);
289        }
290    
291        /**
292         * Adds the field binding to the form. The component will be placed inside a scrollpane.
293         * 
294         * @param binding
295         *            the binding to use
296         * @return an array containing the label, the component of the field binding and the scrollpane and the component of
297         *         the binding which where added to the form
298         * 
299         * @see #createScrollPane(String, JComponent)
300         */
301        public JComponent[] addInScrollPane(Binding binding) {
302            return addInScrollPane(binding, "");
303        }
304    
305        /**
306         * Adds the field binding to the form. The component will be placed inside a scrollpane.
307         * {@link #createScrollPane(String, JComponent)} is used to create the component for the scrollpane
308         * 
309         * @param binding
310         *            the binding to use
311         * @param attributes
312         *            optional layout attributes for the scrollpane. See {@link TableLayoutBuilder} for syntax details
313         * @return an array containing the label, the component of the field binding and the scrollpane and the component of
314         *         the binding which where added to the form
315         * 
316         * @see #createScrollPane(String, JComponent)
317         */
318        public JComponent[] addInScrollPane(Binding binding, String attributes) {
319            Assert.isTrue(getFormModel() == binding.getFormModel(),
320                    "Binding's form model must match FormBuilder's form model");
321            return add(binding.getProperty(), createScrollPane(binding.getProperty(), binding.getControl()), attributes);
322        }
323    
324        /**
325         * Adds a labeled separator to the form.
326         * 
327         * @param text
328         *            the key for the label. Must not be null
329         */
330        public JComponent addSeparator(String text) {
331                    return addSeparator(text, "");
332        }
333    
334        /**
335         * Adds a labeled separator to the form
336         * 
337         * @param text
338         *            the key for the label. Must not be null
339         * @param attributes
340         *            optional attributes. See {@link TableLayoutBuilder} for syntax details
341         */
342        public JComponent addSeparator(String text, String attributes) {
343            JComponent separator = getComponentFactory().createLabeledSeparator(text);
344                    getLayoutBuilder().cell(separator, attributes);
345                    return separator;
346        }
347    
348        /**
349         * Returns the layout builder which is used to build the layout of the added fields and labels
350         * 
351         * @return The form containing the added fields, components and labels in the defined layout. Not null
352         */
353        public TableLayoutBuilder getLayoutBuilder() {
354            if (builder == null) {
355                builder = new TableLayoutBuilder(getComponentFactory().createPanel());
356            }
357            return builder;
358        }
359    
360        /**
361         * Returns the form which has been created by this builder
362         * 
363         * @return The form containing the added fields and labels in the defined layout. Not null
364         */
365        public JComponent getForm() {
366            getBindingFactory().getFormModel().revert();
367            return getLayoutBuilder().getPanel();
368        }
369    
370        /**
371         * returns the default label layout attributes for the form.
372         * 
373         * @return layout attributes for the labels, can be null.
374         */
375        public String getLabelAttributes() {
376            return labelAttributes;
377        }
378    
379        /**
380         * defines the default label layout attributes for the form.
381         * 
382         * @param labelAttributes
383         *            layout attributes for the labels, if null no layout attributes will be applied to the labels. See
384         *            {@link TableLayoutBuilder} for syntax details.
385         */
386        public void setLabelAttributes(String labelAttributes) {
387            this.labelAttributes = labelAttributes;
388        }
389    
390        /**
391         * adds a field binding to the form. This method does not use the default label attributes which may have been set
392         * through {@link #setLabelAttributes(String)}
393         * 
394         * @param binding
395         *            the binding of the field
396         * @param attributes
397         *            optional layout attributes for the label. If null no layout attributes will be applied to the label.
398         *            See {@link TableLayoutBuilder} for syntax details
399         * @return an array containing the label and the component of the binding
400         */
401        public JComponent[] addBinding(Binding binding, String attributes, String labelAttributes) {
402            return addBinding(binding, binding.getControl(), attributes, labelAttributes);
403        }
404    
405        /**
406         * adds a field binding to the form
407         * 
408         * @param binding
409         *            the binding of the field
410         * @param wrappedControl
411         *            the optional wrapped component. If null the component of the binding is used. This Parameter should be
412         *            used if the component of the binding is being wrapped inside this component
413         * @param attributes
414         *            optional layout attributes for the label. If null no layout attributes will be applied to the label.
415         *            See {@link TableLayoutBuilder} for syntax details
416         * @return an array containing the label, the component of the field binding and the wrapped component
417         */
418        public JComponent[] addBinding(Binding binding, JComponent wrappedControl, String attributes) {
419            return addBinding(binding, wrappedControl, attributes, getLabelAttributes());
420        }
421    
422        /**
423         * adds a field binding to the form
424         * 
425         * @param binding
426         *            the binding of the field
427         * @param wrappedComponent
428         *            the optional wrapped component. If null the component of the binding is used. This Parameter should be
429         *            used if the component of the binding is being wrapped inside this component
430         * @param attributes
431         *            optional layout attributes for the wrapped component. If null no layout attributes will be applied to
432         *            the component. See {@link TableLayoutBuilder} for syntax details
433         * @param attributes
434         *            optional layout attributes for the label. If null no layout attributes will be applied to the label.
435         *            See {@link TableLayoutBuilder} for syntax details
436         * @return an array containing the label, the component of the field binding and the wrapped component
437         */
438        public JComponent[] addBinding(Binding binding, JComponent wrappedComponent, String attributes,
439                String labelAttributes) {
440            Assert.notNull(binding, "binding is null");
441            Assert.isTrue(getFormModel() == binding.getFormModel(),
442                    "Binding's form model must match FormBuilder's form model");
443            JComponent component = binding.getControl();
444            final JLabel label = createLabelFor(binding.getProperty(), component);
445            if (wrappedComponent == null) {
446                wrappedComponent = component;
447            }
448            TableLayoutBuilder layoutBuilder = getLayoutBuilder();
449            if (!layoutBuilder.hasGapToLeft()) {
450                layoutBuilder.gapCol();
451            }
452            layoutBuilder.cell(label, labelAttributes);
453            layoutBuilder.labelGapCol();
454            layoutBuilder.cell(wrappedComponent, attributes);
455            return new JComponent[] { label, component, wrappedComponent };
456        }
457    }