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;
017    
018    import java.beans.PropertyChangeEvent;
019    import java.beans.PropertyChangeListener;
020    import java.util.ArrayList;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Map;
024    
025    import javax.swing.JButton;
026    import javax.swing.JComponent;
027    import javax.swing.JRootPane;
028    import javax.swing.SwingUtilities;
029    
030    import org.springframework.binding.form.CommitListener;
031    import org.springframework.binding.form.FormModel;
032    import org.springframework.binding.form.HierarchicalFormModel;
033    import org.springframework.binding.form.ValidatingFormModel;
034    import org.springframework.binding.validation.ValidationListener;
035    import org.springframework.binding.value.IndexAdapter;
036    import org.springframework.binding.value.ValueModel;
037    import org.springframework.binding.value.support.ObservableList;
038    import org.springframework.richclient.command.ActionCommand;
039    import org.springframework.richclient.core.Guarded;
040    import org.springframework.richclient.dialog.Messagable;
041    import org.springframework.richclient.factory.AbstractControlFactory;
042    import org.springframework.richclient.form.binding.BindingFactory;
043    import org.springframework.richclient.form.binding.BindingFactoryProvider;
044    import org.springframework.util.Assert;
045    import org.springframework.util.ClassUtils;
046    import org.springframework.util.StringUtils;
047    
048    /**
049     * Base implementation of a Form.
050     *
051     * Commands provided:
052     * <ul>
053     * <li><em>CommitCommand</em>: wraps the {@link FormModel#commit()} method.
054     * Writes data to backing bean. Guarded mask {@link FormGuard#ON_NOERRORS},
055     * {@link FormGuard#ON_ISDIRTY} and {@link FormGuard#ON_ENABLED}.</li>
056     * <li><em>RevertCommand</em>: wraps the {@link FormModel#revert()} method.
057     * Fall back to the values of the backing bean. Guarded mask
058     * {@link FormGuard#ON_ISDIRTY} and {@link FormGuard#ON_ENABLED}.</li>
059     * <li><em>NewFormObjectCommand</em>: set a fresh instance on the
060     * {@link FormModel}. Guarded mask {@link FormGuard#ON_ENABLED}</li>
061     * </ul>
062     *
063     * All commands provide a securityControllerId.
064     *
065     * @author Keith Donald
066     */
067    public abstract class AbstractForm extends AbstractControlFactory implements Form, CommitListener {
068    
069            private final FormObjectChangeHandler formObjectChangeHandler = new FormObjectChangeHandler();
070    
071            private String formId;
072    
073            private ValidatingFormModel formModel;
074    
075            private HierarchicalFormModel parentFormModel;
076    
077            private FormGuard formGuard;
078    
079            private JButton lastDefaultButton;
080    
081            private PropertyChangeListener formEnabledChangeHandler;
082    
083            private ActionCommand newFormObjectCommand;
084    
085            private ActionCommand commitCommand;
086    
087            private ActionCommand revertCommand;
088    
089            private boolean editingNewFormObject;
090    
091            private boolean clearFormOnCommit = false;
092    
093            private ObservableList editableFormObjects;
094    
095            private ValueModel editingFormObjectIndexHolder;
096    
097            private PropertyChangeListener editingFormObjectSetter;
098    
099            private BindingFactory bindingFactory;
100    
101            private Map childForms = new HashMap();
102    
103            private List validationResultsReporters = new ArrayList();
104    
105            /**
106             * Default constructor will use the uncapitalized simplename of the class to
107             * construct its id.
108             */
109            protected AbstractForm() {
110                    setId(StringUtils.uncapitalize(getClass().getSimpleName()));
111                    init();
112            }
113    
114            /**
115             * Id configurable constructor.
116             */
117            protected AbstractForm(String formId) {
118                    setId(formId);
119                    init();
120            }
121    
122            /**
123             * Convenience constructor which creates a {@link FormModel} by calling
124             * {@link FormModelHelper#createFormModel(Object)}.
125             *
126             * @param formObject object used to create the formModel.
127             * @see #AbstractForm(FormModel)
128             */
129            protected AbstractForm(Object formObject) {
130                    this(FormModelHelper.createFormModel(formObject));
131            }
132    
133            /**
134             * Create an AbstractForm with the given {@link FormModel}. Use the
135             * formModel's id to configure the Form.
136             *
137             * @see #AbstractForm(FormModel, String)
138             */
139            protected AbstractForm(FormModel formModel) {
140                    this(formModel, formModel.getId());
141            }
142    
143            /**
144             * Create an AbstractForm.
145             */
146            protected AbstractForm(FormModel formModel, String formId) {
147                    setId(formId);
148                    if (formModel instanceof ValidatingFormModel) {
149                            setFormModel((ValidatingFormModel) formModel);
150                    }
151                    else {
152                            throw new IllegalArgumentException("Unsupported form model implementation " + formModel);
153                    }
154                    init();
155            }
156    
157            /**
158             * Create a Form with a FormModel that has a child-parent relation with the
159             * provided parentFormModel.
160             *
161             * @param parentFormModel the parent formModel.
162             * @param formId id used for this Form's configuration.
163             * @param childFormObjectPropertyPath the path relative to the
164             * parentFormModel's formObject that leads to the child formObject that will
165             * be handled by this Form.
166             * @see FormModelHelper#createChildPageFormModel(HierarchicalFormModel,
167             * String, String)
168             */
169            protected AbstractForm(HierarchicalFormModel parentFormModel, String formId, String childFormObjectPropertyPath) {
170                    setId(formId);
171                    this.parentFormModel = parentFormModel;
172                    setFormModel(FormModelHelper.createChildPageFormModel(parentFormModel, formId, childFormObjectPropertyPath));
173                    init();
174            }
175    
176            /**
177             * Create a Form with a FormModel that has a child-parent relation with the
178             * provided parentFormModel.
179             *
180             * @param parentFormModel the parent formModel.
181             * @param formId id used for this Form's configuration.
182             * @param childFormObjectHolder the valueModel of the parentFormModel that
183             * holds the child formObject that will be handled by this Form.
184             * @see FormModelHelper#createChildPageFormModel(HierarchicalFormModel,
185             * String, ValueModel)
186             */
187            protected AbstractForm(HierarchicalFormModel parentFormModel, String formId, ValueModel childFormObjectHolder) {
188                    setId(formId);
189                    this.parentFormModel = parentFormModel;
190                    setFormModel(FormModelHelper.createChildPageFormModel(parentFormModel, formId, childFormObjectHolder));
191                    init();
192            }
193    
194            /**
195             * Hook called when constructing the Form.
196             */
197            protected void init() {
198    
199            }
200    
201            public String getId() {
202                    return formId;
203            }
204    
205            /**
206             * Set the id used to configure this Form.
207             */
208            protected void setId(String formId) {
209                    this.formId = formId;
210            }
211    
212            public ValidatingFormModel getFormModel() {
213                    return formModel;
214            }
215    
216            /**
217             * Returns a {@link BindingFactory} bound to the inner {@link FormModel} to
218             * provide binding support.
219             */
220            public BindingFactory getBindingFactory() {
221                    if (bindingFactory == null) {
222                            bindingFactory = ((BindingFactoryProvider) getService(BindingFactoryProvider.class))
223                                            .getBindingFactory(formModel);
224                    }
225                    return bindingFactory;
226            }
227    
228            /**
229             * Set the {@link FormModel} for this {@link Form}. Normally a Form won't
230             * change it's FormModel as this may lead to an inconsistent state. Only use
231             * this when the formModel isn't set yet.
232             *
233             * TODO check why we do allow setting when no control is created.
234             * ValueModels might exist already leading to an inconsistent state.
235             *
236             * @param formModel
237             */
238            protected void setFormModel(ValidatingFormModel formModel) {
239                    Assert.notNull(formModel);
240                    if (this.formModel != null && isControlCreated()) {
241                            throw new UnsupportedOperationException("Cannot reset form model once form control has been created");
242                    }
243                    if (this.formModel != null) {
244                            this.formModel.removeCommitListener(this);
245                    }
246                    this.formModel = formModel;
247                    this.formGuard = new FormGuard(formModel);
248                    this.formModel.addCommitListener(this);
249                    setFormModelDefaultEnabledState();
250            }
251    
252            /**
253             * Returns the parent of this Form's FormModel or <code>null</code>.
254             */
255            protected HierarchicalFormModel getParent() {
256                    return this.parentFormModel;
257            }
258    
259            /**
260             * Add a child (or sub) form to this form. Child forms will be tied in to
261             * the same validation results reporter as this form and they will be
262             * configured to control the same guarded object as this form.
263             * <p>
264             * Validation listeners are unique to a form, so calling
265             * {@link #addValidationListener(ValidationListener)} will only add a
266             * listener to this form. If you want to listen to the child forms, you will
267             * need to add a validation listener on each child form of interest.
268             * <p>
269             * <em>Note:</em> It is very important that the child form provided be
270             * created using a form model that is a child model of this form's form
271             * model. If this is not done, then commit and revert operations will not be
272             * properly delegated to the child form models.
273             *
274             * @param childForm to add
275             */
276            public void addChildForm(Form childForm) {
277                    childForms.put(childForm.getId(), childForm);
278                    getFormModel().addChild(childForm.getFormModel());
279            }
280    
281            /**
282             * @inheritDoc
283             */
284            public List getValidationResultsReporters() {
285                    return validationResultsReporters;
286            }
287    
288            /**
289             * @inheritDoc
290             */
291            public void addValidationResultsReporter(ValidationResultsReporter reporter) {
292                    this.validationResultsReporters.add(reporter);
293            }
294    
295            /**
296             * @inheritDoc
297             */
298            public void removeValidationResultsReporter(ValidationResultsReporter reporter) {
299                    this.validationResultsReporters.remove(reporter);
300            }
301    
302            /**
303             * @inheritDoc
304             */
305            public void removeChildForm(Form childForm) {
306                    getFormModel().removeChild(childForm.getFormModel());
307                    childForms.remove(childForm.getId());
308            }
309    
310            /**
311             * Return a child form of this form with the given form id.
312             * @param id of child form
313             * @return child form, null if no child form with the given id has been
314             * registered
315             */
316            protected Form getChildForm(String id) {
317                    return (Form) childForms.get(id);
318            }
319    
320            protected void setEditableFormObjects(ObservableList editableFormObjects) {
321                    this.editableFormObjects = editableFormObjects;
322            }
323    
324            protected void setEditingFormObjectIndexHolder(ValueModel valueModel) {
325                    this.editingFormObjectIndexHolder = valueModel;
326                    this.editingFormObjectSetter = new EditingFormObjectSetter();
327                    this.editingFormObjectIndexHolder.addValueChangeListener(editingFormObjectSetter);
328            }
329    
330            public boolean isEditingNewFormObject() {
331                    return editingNewFormObject;
332            }
333    
334            /**
335             * Set the "editing new form object" state as indicated.
336             * @param editingNewFormOject
337             */
338            protected void setEditingNewFormObject(boolean editingNewFormOject) {
339                    this.editingNewFormObject = editingNewFormOject;
340            }
341    
342            private class EditingFormObjectSetter implements PropertyChangeListener {
343                    public void propertyChange(PropertyChangeEvent evt) {
344                            int selectionIndex = getEditingFormObjectIndex();
345                            if (selectionIndex == -1) {
346                                    // FIXME: why do we need this
347                                    // getFormModel().reset();
348                                    setEnabled(false);
349                            }
350                            else {
351                                    if (selectionIndex < editableFormObjects.size()) {
352                                            // If we were editing a "new" object, we need to clear
353                                            // that flag since a new object has been selected
354                                            setEditingNewFormObject(false);
355                                            setFormObject(getEditableFormObject(selectionIndex));
356                                            setEnabled(true);
357                                    }
358                            }
359                    }
360            }
361    
362            protected int getEditingFormObjectIndex() {
363                    return ((Integer) editingFormObjectIndexHolder.getValue()).intValue();
364            }
365    
366            protected Object getEditableFormObject(int selectionIndex) {
367                    return editableFormObjects.get(selectionIndex);
368            }
369    
370            public void setClearFormOnCommit(boolean clearFormOnCommit) {
371                    this.clearFormOnCommit = clearFormOnCommit;
372            }
373    
374            protected JButton getDefaultButton() {
375                    if (isControlCreated()) {
376                            JRootPane rootPane = SwingUtilities.getRootPane(getControl());
377                            return rootPane == null ? null : rootPane.getDefaultButton();
378                    }
379                    return null;
380            }
381    
382            protected void setDefaultButton(JButton button) {
383                    JRootPane rootPane = SwingUtilities.getRootPane(getControl());
384                    if (rootPane != null) {
385                            rootPane.setDefaultButton(button);
386                    }
387            }
388    
389            protected final JComponent createControl() {
390                    Assert
391                                    .state(getFormModel() != null,
392                                                    "This form's FormModel cannot be null once control creation is triggered!");
393                    initStandardLocalFormCommands();
394                    JComponent formControl = createFormControl();
395                    this.formEnabledChangeHandler = new FormEnabledPropertyChangeHandler();
396                    getFormModel().addPropertyChangeListener(FormModel.ENABLED_PROPERTY, formEnabledChangeHandler);
397                    addFormObjectChangeListener(formObjectChangeHandler);
398                    if (getCommitCommand() != null) {
399                            getFormModel().addCommitListener(this);
400                    }
401                    return formControl;
402            }
403    
404            private void initStandardLocalFormCommands() {
405                    getNewFormObjectCommand();
406                    getCommitCommand();
407                    getRevertCommand();
408            }
409    
410            /**
411             * Set the form's enabled state based on a default policy--specifically,
412             * disable if the form object is null or the form object is guarded and is
413             * marked as disabled.
414             */
415            protected void setFormModelDefaultEnabledState() {
416                    if (getFormObject() == null) {
417                            getFormModel().setEnabled(false);
418                    }
419                    else {
420                            if (getFormObject() instanceof Guarded) {
421                                    setEnabled(((Guarded) getFormObject()).isEnabled());
422                            }
423                    }
424            }
425    
426            protected abstract JComponent createFormControl();
427    
428            private class FormObjectChangeHandler implements PropertyChangeListener {
429    
430                    public void propertyChange(PropertyChangeEvent evt) {
431                            setFormModelDefaultEnabledState();
432                    }
433            }
434    
435            private class FormEnabledPropertyChangeHandler implements PropertyChangeListener {
436                    public FormEnabledPropertyChangeHandler() {
437                            handleEnabledChange(getFormModel().isEnabled());
438                    }
439    
440                    public void propertyChange(PropertyChangeEvent evt) {
441                            handleEnabledChange(getFormModel().isEnabled());
442                    }
443            }
444    
445            protected void handleEnabledChange(boolean enabled) {
446                    if (enabled) {
447                            if (getCommitCommand() != null) {
448                                    if (lastDefaultButton == null) {
449                                            lastDefaultButton = getDefaultButton();
450                                    }
451                                    getCommitCommand().setDefaultButton();
452                            }
453                    }
454                    else {
455                            if (getCommitCommand() != null) {
456                                    getCommitCommand().setEnabled(false);
457                            }
458                            // set previous default button
459                            if (lastDefaultButton != null) {
460                                    setDefaultButton(lastDefaultButton);
461                            }
462                    }
463            }
464    
465            public ActionCommand getNewFormObjectCommand() {
466                    if (this.newFormObjectCommand == null) {
467                            this.newFormObjectCommand = createNewFormObjectCommand();
468                    }
469                    return newFormObjectCommand;
470            }
471    
472            public ActionCommand getCommitCommand() {
473                    if (this.commitCommand == null) {
474                            this.commitCommand = createCommitCommand();
475                    }
476                    return commitCommand;
477            }
478    
479            public ActionCommand getRevertCommand() {
480                    if (this.revertCommand == null) {
481                            this.revertCommand = createRevertCommand();
482                    }
483                    return revertCommand;
484            }
485    
486            private ActionCommand createNewFormObjectCommand() {
487                    String commandId = getNewFormObjectCommandId();
488                    if (!StringUtils.hasText(commandId)) {
489                            return null;
490                    }
491                    ActionCommand newFormObjectCmd = new ActionCommand(commandId) {
492                            protected void doExecuteCommand() {
493                                    getFormModel().setFormObject(createNewObject());
494                                    getFormModel().setEnabled(true);
495                                    editingNewFormObject = true;
496                                    if (isEditingFormObjectSelected()) {
497                                            setEditingFormObjectIndexSilently(-1);
498                                    }
499                            }
500                    };
501                    newFormObjectCmd.setSecurityControllerId(getNewFormObjectSecurityControllerId());
502                    attachFormGuard(newFormObjectCmd, FormGuard.LIKE_NEWFORMOBJCOMMAND);
503                    return (ActionCommand) getCommandConfigurer().configure(newFormObjectCmd);
504            }
505    
506            /**
507             * Create a new object to install into the form. By default, this simply
508             * returns null. This will cause the form model to instantiate a new copy of
509             * the model object class. Subclasses should override this method if they
510             * need more control over how new objects are constructed.
511             *
512             * @return new object for editing
513             */
514            protected Object createNewObject() {
515                    return null;
516            }
517    
518            private boolean isEditingFormObjectSelected() {
519                    if (editingFormObjectIndexHolder == null) {
520                            return false;
521                    }
522                    int value = ((Integer) editingFormObjectIndexHolder.getValue()).intValue();
523                    return value != -1;
524            }
525    
526            protected void setEditingFormObjectIndexSilently(int index) {
527                    editingFormObjectIndexHolder.removeValueChangeListener(editingFormObjectSetter);
528                    editingFormObjectIndexHolder.setValue(new Integer(index));
529                    editingFormObjectIndexHolder.addValueChangeListener(editingFormObjectSetter);
530            }
531    
532            /**
533             * Returns a command wrapping the commit behavior of the {@link FormModel}.
534             * This command has the guarded and security aspects.
535             */
536            private final ActionCommand createCommitCommand() {
537                    String commandId = getCommitCommandFaceDescriptorId();
538                    if (!StringUtils.hasText(commandId)) {
539                            return null;
540                    }
541                    ActionCommand commitCmd = new ActionCommand(commandId) {
542                            protected void doExecuteCommand() {
543                                    commit();
544                            }
545                    };
546                    commitCmd.setSecurityControllerId(getCommitSecurityControllerId());
547                    attachFormGuard(commitCmd, FormGuard.LIKE_COMMITCOMMAND);
548                    return (ActionCommand) getCommandConfigurer().configure(commitCmd);
549            }
550    
551            public void preCommit(FormModel formModel) {
552            }
553    
554            public void postCommit(FormModel formModel) {
555                    if (editableFormObjects != null) {
556                            if (editingNewFormObject) {
557                                    editableFormObjects.add(formModel.getFormObject());
558                                    setEditingFormObjectIndexSilently(editableFormObjects.size() - 1);
559                            }
560                            else {
561                                    int index = getEditingFormObjectIndex();
562                                    // Avoid updating unless we have actually selected an object for
563                                    // edit
564                                    if (index >= 0) {
565                                            IndexAdapter adapter = editableFormObjects.getIndexAdapter(index);
566                                            adapter.setValue(formModel.getFormObject());
567                                            adapter.fireIndexedObjectChanged();
568                                    }
569                            }
570                    }
571                    if (clearFormOnCommit) {
572                            setFormObject(null);
573                    }
574                    editingNewFormObject = false;
575            }
576    
577            private final ActionCommand createRevertCommand() {
578                    String commandId = getRevertCommandFaceDescriptorId();
579                    if (!StringUtils.hasText(commandId)) {
580                            return null;
581                    }
582                    ActionCommand revertCmd = new ActionCommand(commandId) {
583                            protected void doExecuteCommand() {
584                                    revert();
585                            }
586                    };
587                    attachFormGuard(revertCmd, FormGuard.LIKE_REVERTCOMMAND);
588                    return (ActionCommand) getCommandConfigurer().configure(revertCmd);
589            }
590    
591            protected final JButton createNewFormObjectButton() {
592                    Assert.state(newFormObjectCommand != null, "New form object command has not been created!");
593                    return (JButton) newFormObjectCommand.createButton();
594            }
595    
596            protected final JButton createCommitButton() {
597                    Assert.state(commitCommand != null, "Commit command has not been created!");
598                    return (JButton) commitCommand.createButton();
599            }
600    
601            protected String getNewFormObjectCommandId() {
602                    return "new"
603                                    + StringUtils
604                                                    .capitalize(ClassUtils.getShortName(getFormModel().getFormObject().getClass() + "Command"));
605            }
606    
607            protected String getCommitCommandFaceDescriptorId() {
608                    return null;
609            }
610    
611            protected String getRevertCommandFaceDescriptorId() {
612                    return null;
613            }
614    
615            /**
616             * Subclasses may override to return a security controller id to be attached
617             * to the newFormObject command. The default is
618             * <code>[formModel.id] + "." + [getNewFormObjectCommandId()]</code>.
619             * <p>
620             * This id can be mapped to a specific security controller using the
621             * SecurityControllerManager service.
622             *
623             * @return security controller id, may be null if the face id is null
624             * @see org.springframework.richclient.security.SecurityControllerManager
625             */
626            protected String getNewFormObjectSecurityControllerId() {
627                    return constructSecurityControllerId(getNewFormObjectCommandId());
628            }
629    
630            /**
631             * Subclasses may override to return a security controller id to be attached
632             * to the commit command. The default is The default is
633             * <code>[formModel.id] + "." + [getCommitCommandFaceDescriptorId()]</code>.
634             * <p>
635             * This id can be mapped to a specific security controller using the
636             * SecurityControllerManager service.
637             *
638             * @return security controller id, may be null if the face id is null
639             * @see org.springframework.richclient.security.SecurityControllerManager
640             */
641            protected String getCommitSecurityControllerId() {
642                    return constructSecurityControllerId(getCommitCommandFaceDescriptorId());
643            }
644    
645            /**
646             * Construct a default security controller Id for a given command face id.
647             * The id will be a combination of the form model id, if any, and the face
648             * id.
649             * <p>
650             * <code>[formModel.id] + "." + [commandFaceId]</code> if the form model
651             * id is not null.
652             * <p>
653             * <code>[commandFaceId]</code> if the form model is null.
654             * <p>
655             * <code>null</code> if the commandFaceId is null.
656             * @param commandFaceId
657             * @return default security controller id
658             */
659            protected String constructSecurityControllerId(String commandFaceId) {
660                    String id = null;
661                    String formModelId = getFormModel().getId();
662    
663                    if (commandFaceId != null) {
664                            id = (formModelId != null) ? formModelId + "." + commandFaceId : commandFaceId;
665                    }
666                    return id;
667            }
668    
669            protected void attachFormErrorGuard(Guarded guarded) {
670                    attachFormGuard(guarded, FormGuard.FORMERROR_GUARDED);
671            }
672    
673            protected void attachFormGuard(Guarded guarded, int mask) {
674                    this.formGuard.addGuarded(guarded, mask);
675            }
676    
677            protected void detachFormGuard(Guarded guarded) {
678                    this.formGuard.removeGuarded(guarded);
679            }
680    
681            public Object getFormObject() {
682                    return formModel.getFormObject();
683            }
684    
685            public void setFormObject(Object formObject) {
686                    formModel.setFormObject(formObject);
687            }
688    
689            public Object getValue(String formProperty) {
690                    return formModel.getValueModel(formProperty).getValue();
691            }
692    
693            public ValueModel getValueModel(String formProperty) {
694                    ValueModel valueModel = formModel.getValueModel(formProperty);
695                    if (valueModel == null) {
696                            logger.warn("A value model for property '" + formProperty + "' could not be found.  Typo?");
697                    }
698                    return valueModel;
699            }
700    
701            public boolean isEnabled() {
702                    return this.formModel.isEnabled();
703            }
704    
705            public void setEnabled(boolean enabled) {
706                    this.formModel.setEnabled(enabled);
707            }
708    
709            public void addValidationListener(ValidationListener listener) {
710                    formModel.getValidationResults().addValidationListener(listener);
711            }
712    
713            public void removeValidationListener(ValidationListener listener) {
714                    formModel.getValidationResults().removeValidationListener(listener);
715            }
716    
717            /**
718             * Construct the validation results reporter for this form and attach it to
719             * the provided Guarded object. An instance of
720             * {@link SimpleValidationResultsReporter} will be constructed and returned.
721             * All registered child forms will be attached to the same
722             * <code>guarded</code> and <code>messageReceiver</code> as this form.
723             */
724            public ValidationResultsReporter newSingleLineResultsReporter(Messagable messageReceiver) {
725    
726                    SimpleValidationResultsReporter reporter = new SimpleValidationResultsReporter(
727                                    formModel.getValidationResults(), messageReceiver);
728    
729                    return reporter;
730            }
731    
732            public void addFormObjectChangeListener(PropertyChangeListener listener) {
733                    formModel.getFormObjectHolder().addValueChangeListener(listener);
734            }
735    
736            public void removeFormObjectChangeListener(PropertyChangeListener listener) {
737                    formModel.getFormObjectHolder().removeValueChangeListener(listener);
738            }
739    
740            public void addFormValueChangeListener(String formPropertyPath, PropertyChangeListener listener) {
741                    getFormModel().getValueModel(formPropertyPath).addValueChangeListener(listener);
742            }
743    
744            public void removeFormValueChangeListener(String formPropertyPath, PropertyChangeListener listener) {
745                    getFormModel().getValueModel(formPropertyPath).removeValueChangeListener(listener);
746            }
747    
748            public boolean isDirty() {
749                    return formModel.isDirty();
750            }
751    
752            public boolean hasErrors() {
753                    return formModel.getValidationResults().getHasErrors();
754            }
755    
756            public void commit() {
757                    formModel.commit();
758            }
759    
760            public void revert() {
761                    formModel.revert();
762            }
763    
764            public void reset() {
765                    getFormModel().reset();
766            }
767    
768            public void addGuarded(Guarded guarded) {
769                    formGuard.addGuarded(guarded, FormGuard.FORMERROR_GUARDED);
770            }
771    
772            public void addGuarded(Guarded guarded, int mask) {
773                    formGuard.addGuarded(guarded, mask);
774            }
775    
776            public void removeGuarded(Guarded guarded) {
777                    formGuard.removeGuarded(guarded);
778            }
779    }