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 }