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 }