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.binding.form.support; 017 018 import org.springframework.beans.NotReadablePropertyException; 019 import org.springframework.binding.convert.ConversionException; 020 import org.springframework.binding.convert.ConversionExecutor; 021 import org.springframework.binding.convert.ConversionService; 022 import org.springframework.binding.form.CommitListener; 023 import org.springframework.binding.form.FormModel; 024 import org.springframework.binding.support.BeanPropertyAccessStrategy; 025 import org.springframework.binding.support.TestBean; 026 import org.springframework.binding.support.TestPropertyChangeListener; 027 import org.springframework.binding.value.ValueModel; 028 import org.springframework.binding.value.support.ValueHolder; 029 import org.springframework.richclient.test.SpringRichTestCase; 030 031 import java.beans.PropertyChangeEvent; 032 import java.beans.PropertyChangeListener; 033 034 /** 035 * Tests for 036 * @link AbstractFormModel 037 * 038 * @author Oliver Hutchison 039 */ 040 public abstract class AbstractFormModelTests extends SpringRichTestCase { 041 042 protected AbstractFormModel getFormModel(Object formObject) { 043 return new TestAbstractFormModel(formObject); 044 } 045 046 protected AbstractFormModel getFormModel(BeanPropertyAccessStrategy pas, boolean buffering) { 047 return new TestAbstractFormModel(pas, buffering); 048 } 049 050 protected AbstractFormModel getFormModel(ValueModel valueModel, boolean buffering) { 051 return new TestAbstractFormModel(valueModel, buffering); 052 } 053 054 public void testGetValueModelFromPAS() { 055 TestBean p = new TestBean(); 056 TestPropertyAccessStrategy tpas = new TestPropertyAccessStrategy(p); 057 AbstractFormModel fm = getFormModel(tpas, true); 058 ValueModel vm1 = fm.getValueModel("simpleProperty"); 059 assertEquals(1, tpas.numValueModelRequests()); 060 assertEquals("simpleProperty", tpas.lastRequestedValueModel()); 061 ValueModel vm2 = fm.getValueModel("simpleProperty"); 062 assertEquals(vm1, vm2); 063 assertEquals(1, tpas.numValueModelRequests()); 064 065 try { 066 fm.getValueModel("iDontExist"); 067 fail("should't be able to get value model for invalid property"); 068 } 069 catch (NotReadablePropertyException e) { 070 // exprected 071 } 072 } 073 074 public void testUnbufferedWritesThrough() { 075 TestBean p = new TestBean(); 076 BeanPropertyAccessStrategy pas = new BeanPropertyAccessStrategy(p); 077 AbstractFormModel fm = getFormModel(pas, false); 078 ValueModel vm = fm.getValueModel("simpleProperty"); 079 080 vm.setValue("1"); 081 assertEquals("1", p.getSimpleProperty()); 082 083 vm.setValue(null); 084 assertEquals(null, p.getSimpleProperty()); 085 } 086 087 public void testBufferedDoesNotWriteThrough() { 088 TestBean p = new TestBean(); 089 BeanPropertyAccessStrategy pas = new BeanPropertyAccessStrategy(p); 090 AbstractFormModel fm = getFormModel(pas, true); 091 ValueModel vm = fm.getValueModel("simpleProperty"); 092 093 vm.setValue("1"); 094 assertEquals(null, p.getSimpleProperty()); 095 096 vm.setValue(null); 097 assertEquals(null, p.getSimpleProperty()); 098 } 099 100 public void testDirtyTrackingWithBuffering() { 101 testDirtyTracking(true); 102 } 103 104 public void testDirtyTrackingWithoutBuffering() { 105 testDirtyTracking(false); 106 } 107 108 public void testDirtyTracking(boolean buffering) { 109 TestBean p = new TestBean(); 110 BeanPropertyAccessStrategy pas = new BeanPropertyAccessStrategy(p); 111 TestPropertyChangeListener pcl = new TestPropertyChangeListener(FormModel.DIRTY_PROPERTY); 112 AbstractFormModel fm = getFormModel(pas, buffering); 113 fm.addPropertyChangeListener(FormModel.DIRTY_PROPERTY, pcl); 114 ValueModel vm = fm.getValueModel("simpleProperty"); 115 assertTrue(!fm.isDirty()); 116 117 vm.setValue("2"); 118 assertTrue(fm.isDirty()); 119 assertEquals(1, pcl.eventCount()); 120 121 fm.commit(); 122 assertTrue(!fm.isDirty()); 123 assertEquals(2, pcl.eventCount()); 124 125 vm.setValue("1"); 126 assertTrue(fm.isDirty()); 127 assertEquals(3, pcl.eventCount()); 128 129 fm.setFormObject(new TestBean()); 130 assertTrue(!fm.isDirty()); 131 assertEquals(4, pcl.eventCount()); 132 133 vm.setValue("2"); 134 assertTrue(fm.isDirty()); 135 assertEquals(5, pcl.eventCount()); 136 137 fm.revert(); 138 assertTrue(!fm.isDirty()); 139 assertEquals(6, pcl.eventCount()); 140 } 141 142 /** 143 * Test on dirty state of parent-child relations. When child gets dirty, 144 * parent should also be dirty. When parent reverts, child should revert 145 * too. 146 */ 147 public void testDirtyTracksKids() { 148 TestPropertyChangeListener pcl = new TestPropertyChangeListener(FormModel.DIRTY_PROPERTY); 149 AbstractFormModel pfm = getFormModel(new TestBean()); 150 AbstractFormModel fm = getFormModel(new TestBean()); 151 pfm.addPropertyChangeListener(FormModel.DIRTY_PROPERTY, pcl); 152 pfm.addChild(fm); 153 ValueModel childSimpleProperty = fm.getValueModel("simpleProperty"); 154 ValueModel parentSimpleProperty = pfm.getValueModel("simpleProperty"); 155 // test child property dirty -> parent dirty 156 childSimpleProperty.setValue("1"); 157 assertTrue(pfm.isDirty()); 158 assertEquals(1, pcl.eventCount()); 159 160 fm.revert(); 161 assertTrue(!pfm.isDirty()); 162 assertEquals(2, pcl.eventCount()); 163 // child dirty -> revert parent triggers revert on child 164 childSimpleProperty.setValue("1"); 165 assertTrue(pfm.isDirty()); 166 assertEquals(3, pcl.eventCount()); 167 168 pfm.revert(); 169 assertTrue(!pfm.isDirty()); 170 assertTrue(!fm.isDirty()); 171 assertEquals(4, pcl.eventCount()); 172 // child & parent property dirty -> parent dirty, revert child, then 173 // parent 174 childSimpleProperty.setValue("1"); 175 assertTrue(pfm.isDirty()); 176 assertEquals(5, pcl.eventCount()); 177 178 parentSimpleProperty.setValue("2"); 179 assertTrue(pfm.isDirty()); 180 assertEquals(5, pcl.eventCount()); 181 182 fm.revert(); 183 assertTrue(pfm.isDirty()); 184 assertEquals(5, pcl.eventCount()); 185 186 pfm.revert(); 187 assertTrue(!pfm.isDirty()); 188 assertEquals(6, pcl.eventCount()); 189 } 190 191 public void testSetFormObjectDoesNotRevertChangesToPreviousFormObject() { 192 TestBean p1 = new TestBean(); 193 BeanPropertyAccessStrategy pas = new BeanPropertyAccessStrategy(p1); 194 AbstractFormModel fm = getFormModel(pas, false); 195 fm.getValueModel("simpleProperty").setValue("1"); 196 fm.setFormObject(new TestBean()); 197 assertEquals("1", p1.getSimpleProperty()); 198 } 199 200 public void testCommitEvents() { 201 TestBean p = new TestBean(); 202 BeanPropertyAccessStrategy pas = new BeanPropertyAccessStrategy(p); 203 TestCommitListener cl = new TestCommitListener(); 204 AbstractFormModel fm = getFormModel(pas, false); 205 fm.addCommitListener(cl); 206 ValueModel vm = fm.getValueModel("simpleProperty"); 207 208 vm.setValue("1"); 209 fm.commit(); 210 assertEquals(1, cl.preEditCalls); 211 assertEquals(1, cl.postEditCalls); 212 } 213 214 public void testCommitWritesBufferingThrough() { 215 TestBean p = new TestBean(); 216 BeanPropertyAccessStrategy pas = new BeanPropertyAccessStrategy(p); 217 TestCommitListener cl = new TestCommitListener(); 218 AbstractFormModel fm = getFormModel(pas, true); 219 fm.addCommitListener(cl); 220 ValueModel vm = fm.getValueModel("simpleProperty"); 221 222 vm.setValue("1"); 223 fm.commit(); 224 assertEquals("1", p.getSimpleProperty()); 225 } 226 227 public void testRevertWithBuffering() { 228 testRevert(true); 229 } 230 231 public void testRevertWithoutBuffering() { 232 testRevert(false); 233 } 234 235 public void testRevert(boolean buffering) { 236 TestBean p = new TestBean(); 237 BeanPropertyAccessStrategy pas = new BeanPropertyAccessStrategy(p); 238 TestPropertyChangeListener pcl = new TestPropertyChangeListener(FormModel.DIRTY_PROPERTY); 239 AbstractFormModel fm = getFormModel(pas, buffering); 240 fm.addPropertyChangeListener(FormModel.DIRTY_PROPERTY, pcl); 241 ValueModel vm = fm.getValueModel("simpleProperty"); 242 243 vm.setValue("1"); 244 fm.revert(); 245 246 assertEquals(null, vm.getValue()); 247 assertEquals(null, p.getSimpleProperty()); 248 249 TestBean tb2 = new TestBean(); 250 tb2.setSimpleProperty("tb2"); 251 fm.setFormObject(tb2); 252 253 vm.setValue("1"); 254 fm.revert(); 255 256 assertEquals("tb2", vm.getValue()); 257 assertEquals("tb2", tb2.getSimpleProperty()); 258 } 259 260 public void testEnabledEvents() { 261 TestPropertyChangeListener pcl = new TestPropertyChangeListener(FormModel.ENABLED_PROPERTY); 262 AbstractFormModel fm = getFormModel(new Object()); 263 fm.addPropertyChangeListener(FormModel.ENABLED_PROPERTY, pcl); 264 265 assertTrue(fm.isEnabled()); 266 267 fm.setEnabled(false); 268 assertTrue(!fm.isEnabled()); 269 assertEquals(1, pcl.eventCount()); 270 271 fm.setEnabled(false); 272 assertTrue(!fm.isEnabled()); 273 assertEquals(1, pcl.eventCount()); 274 275 fm.setEnabled(true); 276 assertTrue(fm.isEnabled()); 277 assertEquals(2, pcl.eventCount()); 278 279 fm.setEnabled(true); 280 assertTrue(fm.isEnabled()); 281 assertEquals(2, pcl.eventCount()); 282 } 283 284 public void testEnabledTracksParent() { 285 TestPropertyChangeListener pcl = new TestPropertyChangeListener(FormModel.ENABLED_PROPERTY); 286 AbstractFormModel pfm = getFormModel(new Object()); 287 AbstractFormModel fm = getFormModel(new Object()); 288 fm.addPropertyChangeListener(FormModel.ENABLED_PROPERTY, pcl); 289 pfm.addChild(fm); 290 291 pfm.setEnabled(false); 292 assertTrue(!fm.isEnabled()); 293 assertEquals(1, pcl.eventCount()); 294 295 pfm.setEnabled(true); 296 assertTrue(fm.isEnabled()); 297 assertEquals(2, pcl.eventCount()); 298 299 pfm.setEnabled(false); 300 assertTrue(!fm.isEnabled()); 301 assertEquals(3, pcl.eventCount()); 302 303 fm.setEnabled(false); 304 assertTrue(!fm.isEnabled()); 305 assertEquals(3, pcl.eventCount()); 306 307 pfm.setEnabled(true); 308 assertTrue(!fm.isEnabled()); 309 assertEquals(3, pcl.eventCount()); 310 311 fm.setEnabled(true); 312 assertTrue(fm.isEnabled()); 313 assertEquals(4, pcl.eventCount()); 314 } 315 316 public void testConvertingValueModels() { 317 AbstractFormModel fm = getFormModel(new TestBean()); 318 TestConversionService cs = new TestConversionService(); 319 fm.setConversionService(cs); 320 321 ValueModel vm = fm.getValueModel("simpleProperty", String.class); 322 assertEquals(fm.getValueModel("simpleProperty"), vm); 323 assertEquals(0, cs.calls); 324 325 try { 326 fm.getValueModel("simpleProperty", Integer.class); 327 fail("should have throw IllegalArgumentException"); 328 } 329 catch (IllegalArgumentException e) { 330 // expected 331 } 332 assertEquals(1, cs.calls); 333 assertEquals(String.class, cs.lastSource); 334 assertEquals(Integer.class, cs.lastTarget); 335 336 cs.executer = new ConversionExecutor(String.class, Integer.class, new CopiedPublicNoOpConverter(String.class, 337 Integer.class)); 338 ValueModel cvm = fm.getValueModel("simpleProperty", Integer.class); 339 assertEquals(3, cs.calls); 340 assertEquals(Integer.class, cs.lastSource); 341 assertEquals(String.class, cs.lastTarget); 342 343 assertEquals(fm.getValueModel("simpleProperty", Integer.class), cvm); 344 assertEquals(3, cs.calls); 345 } 346 347 public void testFieldMetadata() { 348 AbstractFormModel fm = getFormModel(new TestBean()); 349 350 assertEquals(String.class, fm.getFieldMetadata("simpleProperty").getPropertyType()); 351 assertTrue(!fm.getFieldMetadata("simpleProperty").isReadOnly()); 352 353 assertEquals(Object.class, fm.getFieldMetadata("readOnly").getPropertyType()); 354 assertTrue(fm.getFieldMetadata("readOnly").isReadOnly()); 355 } 356 357 public void testSetFormObjectUpdatesDirtyState() { 358 final AbstractFormModel fm = getFormModel(new TestBean()); 359 fm.add("simpleProperty"); 360 fm.add("singleSelectListProperty"); 361 362 assertTrue(!fm.isDirty()); 363 364 fm.getValueModel("simpleProperty").addValueChangeListener(new PropertyChangeListener() { 365 366 public void propertyChange(PropertyChangeEvent evt) { 367 fm.getValueModel("singleSelectListProperty").setValue(null); 368 } 369 }); 370 371 TestBean newBean = new TestBean(); 372 newBean.setSimpleProperty("simpleProperty"); 373 newBean.setSingleSelectListProperty("singleSelectListProperty"); 374 fm.setFormObject(newBean); 375 assertEquals(null, fm.getValueModel("singleSelectListProperty").getValue()); 376 assertTrue(fm.isDirty()); 377 fm.getValueModel("singleSelectListProperty").setValue("singleSelectListProperty"); 378 assertTrue(!fm.isDirty()); 379 } 380 381 public void testFormPropertiesAreAccessableFromFormObjectChangeEvents() { 382 final AbstractFormModel fm = getFormModel(new TestBean()); 383 assertEquals(null, fm.getValueModel("simpleProperty").getValue()); 384 TestBean newTestBean = new TestBean(); 385 newTestBean.setSimpleProperty("NewValue"); 386 fm.getFormObjectHolder().addValueChangeListener(new PropertyChangeListener() { 387 388 public void propertyChange(PropertyChangeEvent evt) { 389 assertEquals("NewValue", fm.getValueModel("simpleProperty").getValue()); 390 } 391 }); 392 fm.setFormObject(newTestBean); 393 } 394 395 public void testFormObjectChangeEventComesBeforePropertyChangeEvent() { 396 final TestBean testBean = new TestBean(); 397 final AbstractFormModel fm = getFormModel(testBean); 398 final TestBean newTestBean = new TestBean(); 399 newTestBean.setSimpleProperty("NewValue"); 400 final boolean[] formObjectChangeCalled = new boolean[1]; 401 fm.getFormObjectHolder().addValueChangeListener(new PropertyChangeListener() { 402 403 public void propertyChange(PropertyChangeEvent evt) { 404 formObjectChangeCalled[0] = true; 405 } 406 }); 407 fm.getValueModel("simpleProperty").addValueChangeListener(new PropertyChangeListener() { 408 409 public void propertyChange(PropertyChangeEvent evt) { 410 assertEquals("Form property change event was called before form object change event", true, 411 formObjectChangeCalled[0]); 412 } 413 }); 414 fm.setFormObject(newTestBean); 415 } 416 417 public void testFormObjectChangeEvents() { 418 TestBean testBean = new TestBean(); 419 final AbstractFormModel fm = getFormModel(testBean); 420 TestBean newTestBean = new TestBean(); 421 newTestBean.setSimpleProperty("NewValue"); 422 TestPropertyChangeListener testPCL = new TestPropertyChangeListener(ValueModel.VALUE_PROPERTY); 423 fm.getFormObjectHolder().addValueChangeListener(testPCL); 424 fm.setFormObject(newTestBean); 425 assertEquals(1, testPCL.eventCount()); 426 assertEquals(testBean, testPCL.lastEvent().getOldValue()); 427 assertEquals(newTestBean, testPCL.lastEvent().getNewValue()); 428 } 429 430 public static class TestCommitListener implements CommitListener { 431 int preEditCalls; 432 433 int postEditCalls; 434 435 public void preCommit(FormModel formModel) { 436 preEditCalls++; 437 } 438 439 public void postCommit(FormModel formModel) { 440 postEditCalls++; 441 } 442 } 443 444 public class TestConversionService implements ConversionService { 445 446 public int calls; 447 448 public Class lastSource; 449 450 public Class lastTarget; 451 452 public ConversionExecutor executer; 453 454 public ConversionExecutor getConversionExecutor(Class source, Class target) { 455 calls++; 456 lastSource = source; 457 lastTarget = target; 458 if (executer != null) { 459 return executer; 460 } 461 throw new IllegalArgumentException("no converter found"); 462 } 463 464 public ConversionExecutor getConversionExecutorByTargetAlias(Class arg0, String arg1) 465 throws IllegalArgumentException { 466 fail("this method should never be called"); 467 return null; 468 } 469 470 public Class getClassByAlias(String arg0) { 471 fail("this method should never be called"); 472 return null; 473 } 474 475 public ConversionExecutor[] getConversionExecutorsForSource(Class sourceClass) throws ConversionException { 476 fail("this method should never be called"); 477 return null; 478 } 479 } 480 481 /** 482 * <p> 483 * <b>Summary: </b>Setting a new FormObject should always result in a clean 484 * model (not dirty). Using buffered=<code>true</code>. 485 * </p> 486 * 487 * <p> 488 * This test checks that when a valueModel is dirty and a new FormObject is 489 * set which has the same value for that valueModel, the formModel should 490 * not be dirty. 491 * </p> 492 */ 493 public void testBufferedFormModelSetFormObjectNotDirty() { 494 String someString = "someString"; 495 FormModel model = getFormModel(new TestBean()); 496 ValueModel valueModel = model.getValueModel("simpleProperty"); 497 498 assertEquals("Initial check, formmodel not dirty.", false, model.isDirty()); 499 500 valueModel.setValue(someString); 501 assertEquals("Value changed, model should be dirty.", true, model.isDirty()); 502 503 TestBean newFormObject = new TestBean(); 504 newFormObject.setSimpleProperty(someString); 505 model.setFormObject(newFormObject); 506 assertEquals("New formObject is set, model should not be dirty.", false, model.isDirty()); 507 } 508 509 /** 510 * <p> 511 * <b>Summary: </b>Setting a new FormObject should always result in a clean 512 * model (not dirty). Using buffered=<code>false</code>. 513 * </p> 514 * 515 * <p> 516 * This test checks that when a valueModel is dirty and a new FormObject is 517 * set which has the same value for that valueModel, the formModel should 518 * not be dirty. 519 * </p> 520 */ 521 public void testFormModelSetFormObjectNotDirty() { 522 String someString = "someString"; 523 FormModel model = getFormModel(new ValueHolder(new TestBean()), false); 524 ValueModel valueModel = model.getValueModel("simpleProperty"); 525 526 assertEquals("Initial check, formmodel not dirty.", false, model.isDirty()); 527 528 valueModel.setValue(someString); 529 assertEquals("Value changed, model should be dirty.", true, model.isDirty()); 530 531 TestBean newFormObject = new TestBean(); 532 newFormObject.setSimpleProperty(someString); 533 model.setFormObject(newFormObject); 534 assertEquals("New formObject is set, model should not be dirty.", false, model.isDirty()); 535 } 536 537 /** 538 * <p> 539 * Test whether the enabled state is correctly propagated between 540 * parent-child formModel and that the proper events are fired. 541 * </p> 542 * <p> 543 * In detail: 544 * <ul> 545 * <li>if parent is enabled: should allow child to handle it's own state</li> 546 * <li>if parent is disabled: should override child's enabled state</li> 547 * </ul> 548 * </p> 549 */ 550 public void testParentChildEnabledState() { 551 TestBean formObject = new TestBean(); 552 AbstractFormModel parent = getFormModel(formObject); 553 AbstractFormModel child = getFormModel(formObject); 554 BooleanStatelistener listener = new BooleanStatelistener(FormModel.ENABLED_PROPERTY); 555 listener.state = child.isEnabled(); 556 child.addPropertyChangeListener(FormModel.ENABLED_PROPERTY, listener); 557 558 parent.addChild(child); 559 560 // check if parent->enabled then (child->enabled or child->disabled) 561 parent.setEnabled(true); 562 child.setEnabled(true); 563 assertTrue(listener.state); 564 child.setEnabled(false); 565 assertFalse(listener.state); 566 567 // check if parent->disabled then always child->disabled 568 parent.setEnabled(false); 569 child.setEnabled(false); 570 assertFalse(listener.state); 571 child.setEnabled(true); 572 assertFalse(listener.state); 573 574 parent.removeChild(child); 575 576 // check initial state when adding a child formModel, state should be synchronized at setup and reverted when removing the relation 577 578 // check parent->disabled is correctly overriding child state 579 parent.setEnabled(false); 580 child.setEnabled(true); 581 parent.addChild(child); 582 assertFalse(listener.state); 583 parent.removeChild(child); 584 assertTrue(listener.state); 585 parent.setEnabled(false); 586 child.setEnabled(false); 587 parent.addChild(child); 588 assertFalse(listener.state); 589 parent.removeChild(child); 590 assertFalse(listener.state); 591 592 // check parent->enabled is correctly allowing child state to override 593 parent.setEnabled(true); 594 child.setEnabled(false); 595 parent.addChild(child); 596 assertFalse(listener.state); 597 parent.removeChild(child); 598 assertFalse(listener.state); 599 parent.setEnabled(true); 600 child.setEnabled(true); 601 parent.addChild(child); 602 assertTrue(listener.state); 603 parent.removeChild(child); 604 assertTrue(listener.state); 605 } 606 607 /** 608 * <p> 609 * Test whether the read-only state is correctly propagated between 610 * parent-child formModel and that the proper events are fired. 611 * </p> 612 * <p> 613 * In detail: 614 * <ul> 615 * <li>if parent is readOnly: child should be readOnly</li> 616 * <li>if parent isn't readOnly: child can handle it's own state</li> 617 * </ul> 618 * </p> 619 */ 620 public void testParentChildReadOnlyState() { 621 TestBean formObject = new TestBean(); 622 AbstractFormModel parent = getFormModel(formObject); 623 AbstractFormModel child = getFormModel(formObject); 624 BooleanStatelistener listener = new BooleanStatelistener(FormModel.READONLY_PROPERTY); 625 listener.state = child.isReadOnly(); 626 child.addPropertyChangeListener(FormModel.READONLY_PROPERTY, listener); 627 628 parent.addChild(child); 629 630 // if parent->readOnly then child->readOnly 631 parent.setReadOnly(true); 632 child.setReadOnly(false); 633 assertTrue(listener.state); 634 child.setReadOnly(true); 635 assertTrue(listener.state); 636 637 // if parent->writable then (child->writable or child->readOnly) 638 parent.setReadOnly(false); 639 child.setReadOnly(false); 640 assertFalse(listener.state); 641 child.setReadOnly(true); 642 assertTrue(listener.state); 643 644 parent.removeChild(child); 645 646 // check initial state when adding a child formModel, state should be synchronized at setup and reverted when removing the relation 647 648 // check parent->writable is correctly allowing child to override 649 parent.setReadOnly(false); 650 child.setReadOnly(true); 651 parent.addChild(child); 652 assertTrue(listener.state); 653 parent.removeChild(child); 654 assertTrue(listener.state); 655 parent.setReadOnly(false); 656 child.setReadOnly(false); 657 parent.addChild(child); 658 assertFalse(listener.state); 659 parent.removeChild(child); 660 assertFalse(listener.state); 661 662 // check parent->readOnly is correctly overriding child state 663 parent.setReadOnly(true); 664 child.setReadOnly(false); 665 parent.addChild(child); 666 assertTrue(listener.state); 667 parent.removeChild(child); 668 assertFalse(listener.state); 669 parent.setReadOnly(true); 670 child.setReadOnly(true); 671 parent.addChild(child); 672 assertTrue(listener.state); 673 parent.removeChild(child); 674 assertTrue(listener.state); 675 } 676 677 /** 678 * Listener to register on boolean properties to check if they are in the expected state. 679 */ 680 protected static class BooleanStatelistener implements PropertyChangeListener { 681 682 final String property; 683 684 boolean state = false; 685 686 public BooleanStatelistener(final String property) { 687 this.property = property; 688 } 689 690 public void propertyChange(PropertyChangeEvent evt) { 691 if (property.equals(evt.getPropertyName())) 692 state = Boolean.parseBoolean(evt.getNewValue().toString()); 693 } 694 } 695 696 /** 697 * <p> 698 * Test whether the dirty state is correctly propagated between 699 * parent-child formModel and that the proper events are fired. 700 * </p> 701 * <p> 702 * In detail: 703 * <ul> 704 * <li>if child is dirty then parent MUST be dirty</li> 705 * <li>if child isn't dirty then parent CAN be dirty</li> 706 * </ul> 707 */ 708 public void testParentChildDirtyState() { 709 TestBean formObject = new TestBean(); 710 AbstractFormModel parent = getFormModel(formObject); 711 ValueModel parentValueModel = parent.getValueModel("simpleProperty"); 712 AbstractFormModel child = getFormModel(formObject); 713 ValueModel childValueModel = child.getValueModel("booleanProperty"); 714 BooleanStatelistener listener = new BooleanStatelistener(FormModel.DIRTY_PROPERTY); 715 listener.state = parent.isDirty(); 716 parent.addPropertyChangeListener(FormModel.DIRTY_PROPERTY, listener); 717 718 parent.addChild(child); 719 720 // check if child->dirty then parent->dirty 721 assertFalse(listener.state); 722 childValueModel.setValue(Boolean.TRUE); 723 assertTrue(listener.state); 724 parentValueModel.setValue("x"); 725 assertTrue(listener.state); 726 parentValueModel.setValue(null); // original value 727 assertTrue(listener.state); 728 childValueModel.setValue(Boolean.FALSE); //original value 729 assertFalse(listener.state); 730 731 // check if child->clean then (parent->clean or parent->dirty) 732 parentValueModel.setValue("x"); 733 assertTrue(listener.state); 734 parentValueModel.setValue(null); // original value 735 assertFalse(listener.state); 736 737 parent.removeChild(child); 738 739 // check initial state when adding a child formModel, state should be synchronized at setup and reverted when removing the relation 740 741 // check if dirty child sets parent dirty 742 childValueModel.setValue(Boolean.TRUE); 743 assertFalse(listener.state); 744 parent.addChild(child); 745 assertTrue(listener.state); 746 parent.removeChild(child); 747 assertFalse(listener.state); 748 parentValueModel.setValue("x"); 749 assertTrue(listener.state); 750 parent.addChild(child); 751 assertTrue(listener.state); 752 parent.removeChild(child); 753 assertTrue(listener.state); 754 755 // check if clean child allows parent to override 756 child.revert(); 757 parent.revert(); 758 assertFalse(listener.state); 759 parent.addChild(child); 760 assertFalse(listener.state); 761 parent.removeChild(child); 762 parentValueModel.setValue("x"); 763 assertTrue(listener.state); 764 parent.addChild(child); 765 assertTrue(listener.state); 766 parent.removeChild(child); 767 assertTrue(listener.state); 768 } 769 }