1 /*
2 * Copyright 2006-2016 The JGUIraffe Team.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License")
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.sf.jguiraffe.gui.builder.window.ctrl;
17
18 import javax.swing.event.EventListenerList;
19 import java.util.EventListener;
20 import java.util.HashSet;
21 import java.util.Set;
22
23 import net.sf.jguiraffe.gui.builder.BuilderData;
24 import net.sf.jguiraffe.gui.builder.components.ComponentBuilderData;
25 import net.sf.jguiraffe.gui.builder.event.FormActionEvent;
26 import net.sf.jguiraffe.gui.builder.event.FormActionListener;
27 import net.sf.jguiraffe.gui.builder.event.FormFocusEvent;
28 import net.sf.jguiraffe.gui.builder.event.FormFocusListener;
29 import net.sf.jguiraffe.gui.builder.utils.MessageOutput;
30 import net.sf.jguiraffe.gui.builder.window.Window;
31 import net.sf.jguiraffe.gui.builder.window.WindowBuilderData;
32 import net.sf.jguiraffe.gui.builder.window.WindowEvent;
33 import net.sf.jguiraffe.gui.builder.window.WindowListener;
34 import net.sf.jguiraffe.gui.cmd.Command;
35 import net.sf.jguiraffe.gui.forms.DefaultFormValidatorResults;
36 import net.sf.jguiraffe.gui.forms.Form;
37 import net.sf.jguiraffe.gui.forms.FormValidationMessageFormat;
38 import net.sf.jguiraffe.gui.forms.FormValidator;
39 import net.sf.jguiraffe.gui.forms.FormValidatorResults;
40 import net.sf.jguiraffe.transform.DefaultValidationMessageHandler;
41 import net.sf.jguiraffe.transform.TransformerContext;
42 import net.sf.jguiraffe.transform.ValidationMessageConstants;
43
44 /**
45 * <p>
46 * A base class for form controllers.
47 * </p>
48 * <p>
49 * The form builder library follows the MVC paradigm when dealing with forms:
50 * <ul>
51 * <li>The dialog window displaying the input fields plays the role of the
52 * <em>view</em>.</li>
53 * <li>The <em>model</em> is represented by a data object, the so-called <em>form bean</em>
54 * or <em>model object</em>. This object has
55 * properties that are bound to the input fields of the form. It stores the data
56 * entered by the user. In the initialization phase it provides the data for
57 * filling the input fields. Access to the model object is controlled by a
58 * {@link net.sf.jguiraffe.gui.forms.BindingStrategy BindingStrategy}.</li>
59 * <li>The <em>controller</em> can be an instance of this class or one of its
60 * subclasses. It controls the form's life-cycle and ensures that user input is
61 * validated and correctly saved in the model.</li>
62 * </ul>
63 * </p>
64 * <p>
65 * This class provides a fully functional controller implementation, which
66 * handles all phases of the form's life-cycle. It can be used out of the box.
67 * In most cases adaptation to a specific application's needs is possible by
68 * configuring some of the helper objects used by this class. This way for
69 * instance the validation handling can be changed or the way fields containing
70 * invalid data are displayed. Refer to the documentation of the corresponding
71 * set methods for further details.
72 * </p>
73 * <p>
74 * One of the main tasks of this class is to ensure that input validation is
75 * performed when necessary. When clicking the <em>OK</em> button, a
76 * validation has to be performed in any case. But it is also possible to
77 * perform validation earlier, e.g. when the user left an input field. This can
78 * be achieved by configuring a corresponding <code>FormValidationTrigger</code>.
79 * A <code>FieldMarker</code> is used for defining the appearance of input
80 * fields depending on their validation status.
81 * </p>
82 * <p>
83 * Implementation note: This class is not thread-safe. It is intended to be
84 * associated with a single form instance and not to be used concurrently with
85 * multiple forms or threads.
86 * </p>
87 *
88 * @author Oliver Heger
89 * @version $Id: FormController.java 205 2012-01-29 18:29:57Z oheger $
90 */
91 public class FormController implements WindowListener, FormFocusListener,
92 FormActionListener
93 {
94 /** Constant for the name of the bean with the validation message format. */
95 static final String BEAN_VALIDATION_MESSAGE_FORMAT = "jguiraffe.validationMessageFormat";
96
97 /** Stores the component builder data. */
98 private ComponentBuilderData componentBuilderData;
99
100 /** Stores the window builder data. */
101 private WindowBuilderData windowBuilderData;
102
103 /** Stores the form validation trigger. */
104 private FormValidationTrigger validationTrigger;
105
106 /** A set with the fields that have been visited so far. */
107 private final Set<String> visitedFields;
108
109 /** The list with all event listeners. */
110 private final EventListenerList eventListeners;
111
112 /** Stores a form validator. */
113 private FormValidator formValidator;
114
115 /** Stores the result of the last validation. */
116 private FormValidatorResults lastValidationResults;
117
118 /** Stores a message output object to be used. */
119 private MessageOutput messageOutput;
120
121 /** Stores the object for formatting validation messages. */
122 private FormValidationMessageFormat validationMessageFormat;
123
124 /** The command to be executed when the form is committed.*/
125 private Command okCommand;
126
127 /** The command to be executed when the form is canceled.*/
128 private Command cancelCommand;
129
130 /** Stores the name of the OK button. */
131 private String btnOkName;
132
133 /** Stores the name of the cancel button. */
134 private String btnCancelName;
135
136 /**
137 * Stores the caption of the message box for displaying validation error
138 * messages.
139 */
140 private String validationMessageBoxCaption;
141
142 /** A flag whether the form was committed.*/
143 private boolean committed;
144
145 /**
146 * Creates a new instance of {@code FormController}.
147 */
148 public FormController()
149 {
150 visitedFields = new HashSet<String>();
151 eventListeners = new EventListenerList();
152 }
153
154 /**
155 * Returns the <code>ComponentBuilderData</code> object.
156 *
157 * @return the object with information about all components that belong to
158 * the current form
159 */
160 public ComponentBuilderData getComponentBuilderData()
161 {
162 return componentBuilderData;
163 }
164
165 /**
166 * Sets the <code>ComponentBuilderData</code> object. This object must
167 * have been set before an instance of this class can be used. It allows
168 * access to all components involved and the current form as well.
169 *
170 * @param componentBuilderData the component builder data object
171 */
172 public void setComponentBuilderData(
173 ComponentBuilderData componentBuilderData)
174 {
175 this.componentBuilderData = componentBuilderData;
176 }
177
178 /**
179 * Returns the <code>WindowBuilderData</code> object.
180 *
181 * @return the data object with information about the current window
182 */
183 public WindowBuilderData getWindowBuilderData()
184 {
185 return windowBuilderData;
186 }
187
188 /**
189 * Sets the <code>WindowBuilderData</code> object. This object must have
190 * been set before an instance of this class can be used. It allows access
191 * to important information about the current window including its form
192 * bean.
193 *
194 * @param windowBuilderData the window builder data object
195 */
196 public void setWindowBuilderData(WindowBuilderData windowBuilderData)
197 {
198 this.windowBuilderData = windowBuilderData;
199 }
200
201 /**
202 * Returns a {@code FormValidator} for validating the associated form.
203 * Result can be <b>null</b> if no specific {@code FormValidator} was set.
204 * Note that the result of this method need not always be in sync with the
205 * object returned by {@link Form#getFormValidator()}: this method always
206 * returns the object that was set on a previous
207 * {@link #setFormValidator(FormValidator)} call.
208 *
209 * @return the {@code FormValidator}
210 * @see #setFormValidator(FormValidator)
211 */
212 public FormValidator getFormValidator()
213 {
214 return formValidator;
215 }
216
217 /**
218 * Sets a {@code FormValidator} for the the associated form. This method is
219 * intended to be called during a builder script. When the associated form
220 * is opened, the {@code FormValidator} is automatically installed. If this
221 * method is called later (if the window is already open and the form
222 * exists), the specified in {@code FormValidator} is directly passed to the
223 * {@code Form} object.
224 *
225 * @param formValidator the {@code FormValidator} for the associated {@code
226 * Form}
227 */
228 public void setFormValidator(FormValidator formValidator)
229 {
230 this.formValidator = formValidator;
231
232 if (getComponentBuilderData() != null)
233 {
234 getForm().setFormValidator(formValidator);
235 }
236 }
237
238 /**
239 * Returns the current form bean. This is a convenience method which obtains
240 * the form bean from the {@link WindowBuilderData} object.
241 *
242 * @return the current form bean
243 */
244 public Object getFormBean()
245 {
246 return getWindowBuilderData().getFormBean();
247 }
248
249 /**
250 * Returns the current form. This is a convenience method which obtains the
251 * form from the {@link ComponentBuilderData} object.
252 *
253 * @return the current form
254 */
255 public Form getForm()
256 {
257 return getComponentBuilderData().getForm();
258 }
259
260 /**
261 * Returns the associated window. This is the window containing the form. It
262 * is obtained from the {@link WindowBuilderData} object.
263 *
264 * @return the associated window
265 */
266 public Window getWindow()
267 {
268 return getWindowBuilderData().getResultWindow();
269 }
270
271 /**
272 * Returns the <code>MessageOutput</code> object to be used by this
273 * controller. This can be <b>null</b> if no specific object has been set.
274 *
275 * @return the <code>MessageOutput</code> object
276 * @see #setMessageOutput(MessageOutput)
277 */
278 public MessageOutput getMessageOutput()
279 {
280 return messageOutput;
281 }
282
283 /**
284 * Sets the <code>MessageOutput</code> object to be used by this controller.
285 * The <code>MessageOutput</code> object is used for displaying validation
286 * error messages to the user. Per default the <code>MessageOutput</code>
287 * object defined in the {@link BuilderData} object will be used (which is
288 * typically the application-global output object). With this method it is
289 * possible to set a specific output object for this controller.
290 *
291 * @param messageOutput the <code>MessageOutput</code> object to use
292 * @see #fetchMessageOutput()
293 */
294 public void setMessageOutput(MessageOutput messageOutput)
295 {
296 this.messageOutput = messageOutput;
297 }
298
299 /**
300 * Returns the <code>FormValidationMessageFormat</code> object to be used
301 * by this controller. This can be <b>null</b> if no specific object has
302 * been set.
303 *
304 * @return the <code>FormValidationMessageFormat</code> object
305 * @see #setValidationMessageFormat(FormValidationMessageFormat)
306 */
307 public FormValidationMessageFormat getValidationMessageFormat()
308 {
309 return validationMessageFormat;
310 }
311
312 /**
313 * Sets the <code>FormValidationMessageFormat</code> object to be used by
314 * this controller. This object is used for generating error messages for a
315 * failed validation that are to be displayed to the user. With this method
316 * it is possible to set a specific format object for this purpose. If no
317 * specific object is set, the application-global default format object is
318 * used (which is obtained from the current <code>BeanContext</code>).
319 *
320 * @param validationMessageFormat the
321 * <code>FormValidationMessageFormat</code> object to use
322 * @see #fetchValidationMessageFormat()
323 */
324 public void setValidationMessageFormat(
325 FormValidationMessageFormat validationMessageFormat)
326 {
327 this.validationMessageFormat = validationMessageFormat;
328 }
329
330 /**
331 * Returns the name of the component representing the OK button.
332 *
333 * @return the name of the OK button
334 */
335 public String getBtnOkName()
336 {
337 return btnOkName;
338 }
339
340 /**
341 * Sets the name of the component representing the OK button. This
342 * controller will register an action listener at this component for
343 * handling the commit operation accordingly.
344 *
345 * @param btnOkName the name of the OK button component
346 */
347 public void setBtnOkName(String btnOkName)
348 {
349 this.btnOkName = btnOkName;
350 }
351
352 /**
353 * Returns the name of the component representing the cancel button.
354 *
355 * @return the name of the cancel button
356 */
357 public String getBtnCancelName()
358 {
359 return btnCancelName;
360 }
361
362 /**
363 * Sets the name of the component representing the cancel button. This
364 * controller will register an action listener at this component for
365 * handling the cancel operation accordingly.
366 *
367 * @param btnCancelName the name of the cancel button component
368 */
369 public void setBtnCancelName(String btnCancelName)
370 {
371 this.btnCancelName = btnCancelName;
372 }
373
374 /**
375 * Returns the caption of the message box for displaying validation error
376 * messages.
377 *
378 * @return the caption of the validation error message box
379 */
380 public String getValidationMessageBoxCaption()
381 {
382 return validationMessageBoxCaption;
383 }
384
385 /**
386 * Sets the caption of the message box for displaying validation error
387 * messages. If the user hits the OK button, the data entered by the user is
388 * validated. If this validation fails, a message box with the found
389 * validation problems is displayed. This property allows defining the
390 * caption of this message box. If no caption is set, a default caption is
391 * used (which is defined as a resource ID and resolved using the current
392 * resource manager).
393 *
394 * @param validationMessageBoxCaption the caption of the validation error
395 * message box
396 */
397 public void setValidationMessageBoxCaption(
398 String validationMessageBoxCaption)
399 {
400 this.validationMessageBoxCaption = validationMessageBoxCaption;
401 }
402
403 /**
404 * Returns the <code>FormValidationTrigger</code>.
405 *
406 * @return the validation trigger (can be <b>null</b> if none was set)
407 */
408 public FormValidationTrigger getValidationTrigger()
409 {
410 return validationTrigger;
411 }
412
413 /**
414 * Sets the <code>FormValidationTrigger</code>. This object is called
415 * once in the initialization phase to give it opportunity to register
416 * itself as event listener.
417 *
418 * @param validationTrigger the new validation trigger (can be <b>null</b>)
419 */
420 public void setValidationTrigger(FormValidationTrigger validationTrigger)
421 {
422 this.validationTrigger = validationTrigger;
423 }
424
425 /**
426 * Adds a {@code FormControllerValidationListener} to this controller. The
427 * listener will be notified whenever a validation is performed.
428 *
429 * @param l the listener to be added (must not be <b>null</b>)
430 * @throws IllegalArgumentException if the event listener is <b>null</b>
431 */
432 public void addValidationListener(FormControllerValidationListener l)
433 {
434 addEventListener(l, FormControllerValidationListener.class);
435 }
436
437 /**
438 * Removes the specified {@code FormControllerValidationListener} from this
439 * controller. If the listener is not registered, this method has no effect.
440 *
441 * @param l the listener to be removed
442 */
443 public void removeValidationListener(FormControllerValidationListener l)
444 {
445 eventListeners.remove(FormControllerValidationListener.class, l);
446 }
447
448 /**
449 * Returns an array with all {@code FormControllerValidationListener}
450 * objects registered at this {@code FormController}.
451 *
452 * @return an array with all registered {@code
453 * FormControllerValidationListener} objects
454 */
455 public FormControllerValidationListener[] getValidationListeners()
456 {
457 return eventListeners.getListeners(FormControllerValidationListener.class);
458 }
459
460 /**
461 * Adds a {@code FormControllerFieldStatusListener} to this controller. The
462 * listener will be notified whenever the visited status of a field in the
463 * controller's form changes.
464 *
465 * @param l the listener to be added (must not be <b>null</b>)
466 * @throws IllegalArgumentException if the event listener is <b>null</b>
467 */
468 public void addFieldStatusListener(FormControllerFieldStatusListener l)
469 {
470 addEventListener(l, FormControllerFieldStatusListener.class);
471 }
472
473 /**
474 * Removes the specified {@code FormControllerFieldStatusListener} from this
475 * controller. If the listener is not registered, this method has no effect.
476 *
477 * @param l the listener to be removed
478 */
479 public void removeFieldStatusListener(FormControllerFieldStatusListener l)
480 {
481 eventListeners.remove(FormControllerFieldStatusListener.class, l);
482 }
483
484 /**
485 * Returns an array with all {@code FormControllerFieldStatusListener}
486 * objects registered at this {@code FormController}.
487 *
488 * @return an array with all registered {@code
489 * FormControllerFieldStatusListener} objects
490 */
491 public FormControllerFieldStatusListener[] getFieldStatusListeners()
492 {
493 return eventListeners.getListeners(FormControllerFieldStatusListener.class);
494 }
495
496 /**
497 * Adds a {@code FormControllerFormListener} to this controller. The
498 * listener will be notified when the form associated with this controller
499 * is closed.
500 *
501 * @param l the listener to be added (must not be <b>null</b>)
502 * @throws IllegalArgumentException if the listener is <b>null</b>
503 */
504 public void addFormListener(FormControllerFormListener l)
505 {
506 addEventListener(l, FormControllerFormListener.class);
507 }
508
509 /**
510 * Removes the specified {@code FormControllerFormListener} from this
511 * controller. If the listener is not registered, this method has no effect.
512 *
513 * @param l the listener to be removed
514 */
515 public void removeFormListener(FormControllerFormListener l)
516 {
517 eventListeners.remove(FormControllerFormListener.class, l);
518 }
519
520 /**
521 * Returns an array with all {@code FormControllerFormListener} objects
522 * registered at this {@code FormController}.
523 *
524 * @return an array with all registered {@code FormControllerFormListener}
525 * objects
526 */
527 public FormControllerFormListener[] getFormListeners()
528 {
529 return eventListeners.getListeners(FormControllerFormListener.class);
530 }
531
532 /**
533 * Returns the command to be executed when the form is closed in reaction of
534 * the OK button.
535 *
536 * @return the command to be executed after OK was clicked
537 */
538 public Command getOkCommand()
539 {
540 return okCommand;
541 }
542
543 /**
544 * Sets the command to be executed when the form is closed in reaction of
545 * the OK button. When the user commits a form often some actions have to be
546 * performed (e.g. saving some data in the database, triggering some other
547 * components, etc.). These actions can be implemented as a
548 * <code>{@link Command}</code> object and associated with this
549 * controller. In its action handler for the OK event the controller will
550 * check (after a successful validation) whether a command was set. If this
551 * is the case, it will be passed to the current command queue.
552 *
553 * @param okCommand the command to be executed when the form is committed
554 */
555 public void setOkCommand(Command okCommand)
556 {
557 this.okCommand = okCommand;
558 }
559
560 /**
561 * Returns the command to be executed when the form is canceled.
562 *
563 * @return the command to be executed after the cancel button was clicked
564 */
565 public Command getCancelCommand()
566 {
567 return cancelCommand;
568 }
569
570 /**
571 * Sets the command to be executed when the form is canceled. This method is
572 * similar to <code>{@link #setOkCommand(Command)}</code>, but it allows
573 * to associate a <code>{@link Command}</code> object with the cancel
574 * button (or any other close action that does not mean a commit). This way
575 * it is possible to execute some action when the user decides to throw away
576 * its input.
577 *
578 * @param cancelCommand the command to be executed when the form is canceled
579 */
580 public void setCancelCommand(Command cancelCommand)
581 {
582 this.cancelCommand = cancelCommand;
583 }
584
585 /**
586 * Performs a validation of the associated form. After that the
587 * {@link FormControllerValidationListener} objects registered at this
588 * controller will be notified.
589 *
590 * @return a data object with information about the result of the validation
591 */
592 public FormValidatorResults validate()
593 {
594 FormValidatorResults results = getForm().validate(getFormBean());
595 lastValidationResults = results;
596 fireValidationEvent(results);
597 return results;
598 }
599
600 /**
601 * Performs a validation of the associated form and displays validation
602 * messages if this is not successful. This method delegates to
603 * {@link #validate()}. If validation results indicate errors, a message
604 * window is displayed containing corresponding validation error messages.
605 * This method is intended to do a validation in reaction on a user action,
606 * e.g. when the user clicks an <em>apply</em> button.
607 *
608 * @return a data object with information about the result of the validation
609 * @since 1.3.1
610 */
611 public FormValidatorResults validateAndDisplayMessages()
612 {
613 markFieldsAsVisited();
614 FormValidatorResults results = validate();
615 if (!results.isValid())
616 {
617 String msg =
618 fetchValidationMessageFormat().format(results, getForm());
619 fetchMessageOutput().show(getWindow(), msg,
620 fetchValidationMessageBoxCaption(),
621 MessageOutput.MESSAGE_ERROR, MessageOutput.BTN_OK);
622 }
623 return results;
624 }
625
626 /**
627 * Returns the results of the last validation operation. The object returned
628 * by this method is the same as was returned by the last
629 * {@link #validate()} call. This is useful for instance to determine which
630 * input fields are currently invalid. If no validation has been performed
631 * so far, a valid result object is returned.
632 *
633 * @return a {@code FormValidatorResults} object with the last validation
634 * results
635 */
636 public FormValidatorResults getLastValidationResults()
637 {
638 return (lastValidationResults != null) ? lastValidationResults
639 : DefaultFormValidatorResults.validResultsForForm(getForm());
640 }
641
642 /**
643 * Dummy implementation of this window event.
644 *
645 * @param event the received event
646 */
647 public void windowActivated(WindowEvent event)
648 {
649 }
650
651 /**
652 * The associated window was closed. This implementation checks whether
653 * commands were registered for either the OK or the cancel button. If this
654 * is the case, the correct command is executed. Also, registered
655 * {@link FormControllerFormListener} objects are notified.
656 *
657 * @param event the received event
658 */
659 public void windowClosed(WindowEvent event)
660 {
661 Command cmd = isCommitted() ? getOkCommand() : getCancelCommand();
662 if (cmd != null)
663 {
664 getBuilderData().getCommandQueue().execute(cmd);
665 }
666
667 fireFormEvent();
668 }
669
670 /**
671 * Dummy implementation of this window event.
672 *
673 * @param event the received event
674 */
675 public void windowDeactivated(WindowEvent event)
676 {
677 }
678
679 /**
680 * Dummy implementation of this window event.
681 *
682 * @param event the received event
683 */
684 public void windowDeiconified(WindowEvent event)
685 {
686 }
687
688 /**
689 * Dummy implementation of this window event.
690 *
691 * @param event the received event
692 */
693 public void windowIconified(WindowEvent event)
694 {
695 }
696
697 /**
698 * Dummy implementation of this window event.
699 *
700 * @param event the received event
701 */
702 public void windowClosing(WindowEvent event)
703 {
704 }
705
706 /**
707 * The window containing the associated form was opened. This is the main
708 * initialization method. The controller has to perform some setup here.
709 *
710 * @param event the event
711 * @throws IllegalStateException if a required field is missing
712 */
713 public void windowOpened(WindowEvent event)
714 {
715 checkRequiredFields();
716
717 getComponentBuilderData().getEventManager().addFocusListener(this);
718 registerActionListener(getBtnOkName());
719 registerActionListener(getBtnCancelName());
720
721 if (getValidationTrigger() != null)
722 {
723 getValidationTrigger().initTrigger(this);
724 }
725
726 if (getFormValidator() != null)
727 {
728 getForm().setFormValidator(getFormValidator());
729 }
730 initFormFields();
731 validate();
732 }
733
734 /**
735 * A component of the associated window was given the focus.
736 *
737 * @param e the focus event
738 */
739 public void focusGained(FormFocusEvent e)
740 {
741 }
742
743 /**
744 * A component of the associated window lost the focus. We track this event
745 * to mark the field as visited. This has an influence on validation. If the
746 * visited status for this field has changed, the {@code FieldMarker} also
747 * needs to be notified.
748 *
749 * @param e the focus event
750 */
751 public void focusLost(FormFocusEvent e)
752 {
753 if (visitedFields.add(e.getName()))
754 {
755 // the field was visited for the first time => status change
756 fireFieldStatusEvent(e.getName());
757 }
758 }
759
760 /**
761 * Processes action events. This method tests whether the event was caused
762 * by the OK or the cancel button. If this is the case, the corresponding
763 * processing method is called.
764 *
765 * @param e the event
766 * @see #okButtonClicked(FormActionEvent)
767 * @see #cancelButtonClicked(FormActionEvent)
768 */
769 public void actionPerformed(FormActionEvent e)
770 {
771 if (e.getName() != null)
772 {
773 if (e.getName().equals(getBtnCancelName()))
774 {
775 cancelButtonClicked(e);
776 }
777 else if (e.getName().equals(getBtnOkName()))
778 {
779 okButtonClicked(e);
780 }
781 }
782 }
783
784 /**
785 * Tests whether the field with the given name has already been visited by
786 * the user.
787 *
788 * @param name the name of the field
789 * @return a flag whether this field has already been visited
790 */
791 public boolean isFieldVisited(String name)
792 {
793 return visitedFields.contains(name);
794 }
795
796 /**
797 * Returns the current <code>BuilderData</code> object. This object can be
798 * used for gaining access to some application global objects and the
799 * complete configuration of the builder.
800 *
801 * @return the <code>BuilderData</code> object
802 */
803 protected BuilderData getBuilderData()
804 {
805 return (BuilderData) getComponentBuilderData().getBeanContext()
806 .getBean(ComponentBuilderData.KEY_BUILDER_DATA);
807 }
808
809 /**
810 * Returns the current <code>MessageOutput</code> object. If an output
811 * object was set explicitly using the <code>setMessageOutput()</code>
812 * method, this object will be returned. Otherwise this method obtains the
813 * <code>MessageOutput</code> object from the current
814 * <code>{@link BuilderData}</code> object.
815 *
816 * @return the <code>MessageOutput</code> object to be used
817 */
818 protected MessageOutput fetchMessageOutput()
819 {
820 MessageOutput result = getMessageOutput();
821
822 if (result == null)
823 {
824 result = getBuilderData().getMessageOutput();
825 }
826
827 return result;
828 }
829
830 /**
831 * Obtains the <code>FormValidationMessageFormat</code> object to be used.
832 * If a format object was set explicitly using the
833 * <code>setValidationMessageFormat()</code> method, this object will be
834 * returned. Otherwise this method queries the current
835 * <code>BeanContext</code> for the default format object.
836 *
837 * @return the <code>FormValidationMessageFormat</code> object to be used
838 */
839 protected FormValidationMessageFormat fetchValidationMessageFormat()
840 {
841 FormValidationMessageFormat fmt = getValidationMessageFormat();
842
843 if (fmt == null)
844 {
845 fmt = (FormValidationMessageFormat) getComponentBuilderData()
846 .getBeanContext().getBean(BEAN_VALIDATION_MESSAGE_FORMAT);
847 }
848
849 return fmt;
850 }
851
852 /**
853 * Returns the caption for a message box for displaying validation error
854 * messages. This method checks whether such a caption was explicitly set
855 * (using the <code>setValidationMessageBoxCaption()</code>). If this is
856 * the case, it is returned. Otherwise the resource key for the default
857 * caption is resolved.
858 *
859 * @return the caption for the message box for validation error messages
860 */
861 protected String fetchValidationMessageBoxCaption()
862 {
863 String caption = getValidationMessageBoxCaption();
864
865 if (caption == null)
866 {
867 TransformerContext tctx = getBuilderData().getTransformerContext();
868 caption = tctx
869 .getResourceManager()
870 .getText(
871 tctx.getLocale(),
872 DefaultValidationMessageHandler.DEFAULT_RESOURCE_GROUP_NAME,
873 ValidationMessageConstants.ERR_MESSAGE_CAPTION);
874 }
875
876 return caption;
877 }
878
879 /**
880 * The OK button was clicked. This method performs a validation. If this is
881 * successful, the form is closed. Otherwise an error message is created
882 * using the {@link FormValidationMessageFormat} object and
883 * displayed to the user via the current {@link MessageOutput}
884 * object. When the user clicks OK, he or she indicates that all fields have
885 * been properly filled in; so they are marked as visited.
886 *
887 * @param event the event object that triggered this method call
888 */
889 protected void okButtonClicked(FormActionEvent event)
890 {
891 FormValidatorResults vres = validateAndDisplayMessages();
892
893 if (vres.isValid())
894 {
895 committed = true;
896 closeForm();
897 }
898 }
899
900 /**
901 * The cancel button was clicked. This will cause the form to be closed
902 * without a validation.
903 *
904 * @param event the event object that triggered this method call
905 */
906 protected void cancelButtonClicked(FormActionEvent event)
907 {
908 closeForm();
909 }
910
911 /**
912 * Closes the associated form. This method is called by both the action
913 * handler of the OK and the cancel button when the form can be closed. It
914 * invokes the <code>close()</code> method on the associated window
915 * object.
916 */
917 protected void closeForm()
918 {
919 getWindow().close(true);
920 }
921
922 /**
923 * Returns a flag whether the form was committed. This method can be used to
924 * find out whether the form was closed using the OK button (in this case
925 * the return value is <b>true</b>) or whether it was canceled. Of course,
926 * calling this method makes only sense after the form was closed. It is
927 * invoked by the handler for the window closed event to find out, which
928 * command object is to be executed (if any).
929 *
930 * @return a flag whether the form was closed using the OK button
931 */
932 protected boolean isCommitted()
933 {
934 return committed;
935 }
936
937 /**
938 * Notifies all registered validation listeners about a validation
939 * operation.
940 *
941 * @param results the validation results
942 */
943 protected void fireValidationEvent(FormValidatorResults results)
944 {
945 FormControllerValidationEvent event = null;
946 Object[] listeners = eventListeners.getListenerList();
947
948 for (int i = listeners.length - 2; i >= 0; i -= 2)
949 {
950 if (listeners[i] == FormControllerValidationListener.class)
951 {
952 if (event == null)
953 {
954 event = new FormControllerValidationEvent(this, results);
955 }
956 ((FormControllerValidationListener) listeners[i + 1])
957 .validationPerformed(event);
958 }
959 }
960 }
961
962 /**
963 * Notifies all registered field status listeners about a change in the
964 * status of a field.
965 *
966 * @param fieldName the name of the affected field
967 */
968 protected void fireFieldStatusEvent(String fieldName)
969 {
970 FormControllerFieldStatusEvent event = null;
971 Object[] listeners = eventListeners.getListenerList();
972
973 for (int i = listeners.length - 2; i >= 0; i -= 2)
974 {
975 if (listeners[i] == FormControllerFieldStatusListener.class)
976 {
977 if (event == null)
978 {
979 event = new FormControllerFieldStatusEvent(this, fieldName);
980 }
981 ((FormControllerFieldStatusListener) listeners[i + 1])
982 .fieldStatusChanged(event);
983 }
984 }
985 }
986
987 /**
988 * Notifies all registered form listeners that the form has been closed.
989 */
990 protected void fireFormEvent()
991 {
992 FormControllerFormEvent event = null;
993 Object[] listeners = eventListeners.getListenerList();
994
995 for (int i = listeners.length - 2; i >= 0; i -= 2)
996 {
997 if (listeners[i] == FormControllerFormListener.class)
998 {
999 if (event == null)
1000 {
1001 event = createFormEvent();
1002 }
1003 ((FormControllerFormListener) listeners[i + 1])
1004 .formClosed(event);
1005 }
1006 }
1007 }
1008
1009 /**
1010 * Tests whether all required fields are set. If this is not the case, an
1011 * exception will be thrown.
1012 *
1013 * @throws IllegalStateException if a required field is missing
1014 */
1015 private void checkRequiredFields()
1016 {
1017 if (getComponentBuilderData() == null)
1018 {
1019 throw new IllegalStateException("No component builder data is set!");
1020 }
1021 if (getWindowBuilderData() == null)
1022 {
1023 throw new IllegalStateException("No window builder data is set!");
1024 }
1025 if (getWindowBuilderData().getResultWindow() == null)
1026 {
1027 throw new IllegalStateException("No associated window found!");
1028 }
1029 }
1030
1031 /**
1032 * Registers this controller as action listener at the specified component.
1033 * This is only done if the passed in name is not <b>null</b>.
1034 *
1035 * @param name the name of the component
1036 */
1037 private void registerActionListener(String name)
1038 {
1039 if (name != null)
1040 {
1041 getComponentBuilderData().getEventManager().addActionListener(name,
1042 this);
1043 }
1044 }
1045
1046 /**
1047 * Initializes the fields of the form with the data of the form bean. If no
1048 * form bean is specified, no initialization will be performed.
1049 */
1050 private void initFormFields()
1051 {
1052 getForm().initFields(getFormBean());
1053 }
1054
1055 /**
1056 * Marks all form fields as visited. This method is called when the user
1057 * presses the OK button.
1058 */
1059 private void markFieldsAsVisited()
1060 {
1061 for (String fld : getForm().getFieldNames())
1062 {
1063 visitedFields.add(fld);
1064 }
1065 }
1066
1067 /**
1068 * Helper method for adding an event listener. The listener is checked for
1069 * <b>null</b> and then added to the central event listener list.
1070 *
1071 * @param <T> the type of the listener
1072 * @param l the listener to be added (must not be <b>null</b>)
1073 * @param listenerClass the event listener class
1074 */
1075 private <T extends EventListener> void addEventListener(T l,
1076 Class<T> listenerClass)
1077 {
1078 if (l == null)
1079 {
1080 throw new IllegalArgumentException(
1081 "Event listener must not be null!");
1082 }
1083 eventListeners.add(listenerClass, l);
1084 }
1085
1086 /**
1087 * Creates a {@code FormControllerFormEvent} based on the current committed
1088 * status.
1089 *
1090 * @return the new event
1091 */
1092 private FormControllerFormEvent createFormEvent()
1093 {
1094 return new FormControllerFormEvent(this,
1095 isCommitted() ? FormControllerFormEvent.Type.FORM_COMMITTED
1096 : FormControllerFormEvent.Type.FORM_CANCELED);
1097 }
1098 }