1   /*
2    * Copyright 2002-2004 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * 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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package org.springframework.binding.form.support;
17  
18  import org.springframework.binding.convert.ConversionContext;
19  import org.springframework.binding.convert.ConversionException;
20  import org.springframework.binding.convert.ConversionExecutor;
21  import org.springframework.binding.convert.Converter;
22  import org.springframework.binding.form.BindingErrorMessageProvider;
23  import org.springframework.binding.form.FormModel;
24  import org.springframework.binding.form.ValidatingFormModel;
25  import org.springframework.binding.support.BeanPropertyAccessStrategy;
26  import org.springframework.binding.support.TestBean;
27  import org.springframework.binding.support.TestPropertyChangeListener;
28  import org.springframework.binding.validation.ValidationMessage;
29  import org.springframework.binding.validation.ValidationResults;
30  import org.springframework.binding.validation.ValidationResultsModel;
31  import org.springframework.binding.validation.Validator;
32  import org.springframework.binding.validation.support.DefaultValidationMessage;
33  import org.springframework.binding.validation.support.DefaultValidationResults;
34  import org.springframework.binding.value.ValueModel;
35  import org.springframework.binding.value.support.ValueHolder;
36  import org.springframework.richclient.core.Severity;
37  
38  import java.util.Set;
39  
40  /**
41   * Tests for @link DefaultFormModel
42   *
43   * @author Oliver Hutchison
44   */
45  public class DefaultFormModelTests extends AbstractFormModelTests {
46  
47      protected AbstractFormModel getFormModel(Object formObject) {
48          return new TestDefaultFormModel(formObject);
49      }
50  
51      protected AbstractFormModel getFormModel(BeanPropertyAccessStrategy pas, boolean buffering) {
52          return new TestDefaultFormModel(pas, buffering);
53      }
54  
55      public void testPropertyChangeCausesValidation() {
56          DefaultFormModel fm = (DefaultFormModel) getFormModel(new TestBean());
57          TestValidator v = new TestValidator();
58          fm.setValidator(v);
59          TestConversionService cs = new TestConversionService();
60          cs.executer = new ConversionExecutor(String.class, String.class, new CopiedPublicNoOpConverter(String.class, String.class));
61          fm.setConversionService(cs);
62          ValueModel vm = fm.getValueModel("simpleProperty");
63          // starting at 2: constructing a formmodel + creating valueModel
64          int expectedCount = 2;
65          assertEquals(expectedCount++, v.count);
66  
67          vm.setValue("1");
68          assertEquals(expectedCount, v.count);
69  
70          // no change in value, no validation triggered.
71          vm.setValue("1");
72          assertEquals(expectedCount++, v.count);
73  
74          vm.setValue(null);
75          assertEquals(expectedCount++, v.count);
76  
77          vm = fm.getValueModel("simpleProperty", Integer.class);
78          vm.setValue("1");
79          assertEquals(expectedCount++, v.count);
80  
81          vm.setValue("2");
82          assertEquals(expectedCount++, v.count);
83      }
84  
85      public void testValidationMessages() {
86      	DefaultFormModel fm = (DefaultFormModel) getFormModel(new TestBean());
87          ValidationResultsModel r = fm.getValidationResults();
88          TestValidator v = new TestValidator();
89          fm.setValidator(v);
90          ValueModel vm = fm.getValueModel("simpleProperty");
91          // starting at 2: constructing a formmodel + creating valueModel
92          int expectedCount = 2;
93  
94          assertEquals(expectedCount++, v.count);
95          assertEquals(0, r.getMessageCount());
96  
97          v.results = getValidationResults("message1");
98          vm.setValue("1");
99          assertEquals(expectedCount++, v.count);
100         assertEquals(1, r.getMessageCount());
101         assertContainsMessage("message1", r.getMessages());
102 
103         v.results = getValidationResults("message2");
104         vm.setValue("2");
105         assertEquals(expectedCount, v.count);
106         assertEquals(1, r.getMessageCount());
107         assertContainsMessage("message2", r.getMessages());
108         // this will cause a binding exception
109         vm.setValue(new Object());
110         assertEquals(expectedCount++, v.count);
111         assertEquals(2, r.getMessageCount());
112         assertContainsMessage("message2", r.getMessages());
113 
114         // this will clear the binding exception
115         vm.setValue("3");
116         assertEquals(expectedCount++, v.count);
117         assertEquals(1, r.getMessageCount());
118         assertContainsMessage("message2", r.getMessages());
119 
120         fm.validate();
121         assertEquals(expectedCount++, v.count);
122         assertEquals(1, r.getMessageCount());
123         assertContainsMessage("message2", r.getMessages());
124     }
125 
126     public void testRaiseClearValidationMessage() {
127     	DefaultFormModel fm = (DefaultFormModel) getFormModel(new TestBean());
128         ValidationResultsModel r = fm.getValidationResults();
129         TestValidator v = new TestValidator();
130         fm.setValidator(v);
131         ValueModel vm = fm.getValueModel("simpleProperty");
132         // starting at 2: constructing a formmodel + creating valueModel
133         int expectedCount = 2;
134 
135         final DefaultValidationMessage message1 = new DefaultValidationMessage("simpleProperty", Severity.ERROR, "1");
136 
137         fm.raiseValidationMessage(message1);
138         assertEquals(expectedCount++, v.count);
139         assertEquals(1, r.getMessageCount());
140         assertContainsMessage("1", r.getMessages());
141 
142         fm.clearValidationMessage(message1);
143         assertEquals(0, r.getMessageCount());
144 
145         fm.raiseValidationMessage(message1);
146         fm.setValidating(false);
147         assertEquals(0, r.getMessageCount());
148 
149         fm.setValidating(true);
150         assertEquals(expectedCount++, v.count);
151         assertEquals(1, r.getMessageCount());
152 
153         v.results = getValidationResults("2");
154         vm.setValue("3");
155         assertEquals(expectedCount++, v.count);
156         assertEquals(2, r.getMessageCount());
157 
158         fm.clearValidationMessage(message1);
159         assertEquals(1, r.getMessageCount());
160     }
161 
162     public void testChangingValidatingClearsMessagesOrValidates() {
163         DefaultFormModel fm = (DefaultFormModel) getFormModel(new TestBean());
164         ValidationResultsModel r = fm.getValidationResults();
165         TestValidator v = new TestValidator();
166         // starting at 2: constructing a formmodel + creating valueModel
167         int expectedCount = 2;
168         v.results = getValidationResults("message1");
169         fm.setValidator(v);
170 
171         ValueModel vm = fm.getValueModel("simpleProperty");
172         assertEquals(expectedCount, v.count);
173         assertEquals(1, r.getMessageCount());
174 
175         fm.setValidating(false);
176         assertEquals(expectedCount++, v.count);
177         assertEquals(0, r.getMessageCount());
178 
179         fm.setValidating(true);
180         assertEquals(expectedCount, v.count);
181         assertEquals(1, r.getMessageCount());
182 
183         // this will cause a binding exception
184         vm.setValue(new Object());
185         assertEquals(expectedCount, v.count);
186         assertEquals(2, r.getMessageCount());
187 
188         fm.setValidating(false);
189         assertEquals(expectedCount, v.count);
190         assertEquals(0, r.getMessageCount());
191 
192         // this will cause a another binding exception
193         fm.getValueModel("listProperty").setValue(new Object());
194         assertEquals(expectedCount, v.count);
195         assertEquals(0, r.getMessageCount());
196 
197         vm.setValue("test");
198         assertEquals(expectedCount++, v.count);
199         assertEquals(0, r.getMessageCount());
200 
201         fm.setValidating(true);
202         assertEquals(expectedCount++, v.count);
203         assertEquals(2, r.getMessageCount());
204     }
205 
206     public void testSetThrowsExceptionRaisesValidationMessage() {
207         final ErrorBean errorBean = new ErrorBean();
208         DefaultFormModel fm = (DefaultFormModel) getFormModel(errorBean);
209         final ValueModel vm = fm.getValueModel("error");
210 
211         vm.setValue("test");
212         assertEquals(1, fm.getValidationResults().getMessageCount());
213 
214         errorBean.errorToThrow = null;
215         vm.setValue("test");
216         assertEquals(0, fm.getValidationResults().getMessageCount());
217     }
218 
219     public void testTypeConversionThrowsExceptionRaisesValidationMessage() {
220         DefaultFormModel fm = (DefaultFormModel) getFormModel(new TestBean());
221         TestConversionService cs = new TestConversionService();
222         cs.executer = new ConversionExecutor(String.class, Integer.class, new ExceptionConverter(String.class,
223                 Integer.class));
224         fm.setConversionService(cs);
225         final ValueModel vm = fm.getValueModel("simpleProperty", Integer.class);
226 
227         vm.setValue("test");
228         assertEquals(1, fm.getValidationResults().getMessageCount());
229     }
230 
231     public void testValidatingEvents() {
232         TestPropertyChangeListener pcl = new TestPropertyChangeListener(ValidatingFormModel.VALIDATING_PROPERTY);
233         DefaultFormModel fm = (DefaultFormModel) getFormModel(new TestBean());
234         fm.addPropertyChangeListener(ValidatingFormModel.VALIDATING_PROPERTY, pcl);
235         assertTrue(fm.isEnabled());
236 
237         fm.setValidating(false);
238         assertTrue(!fm.isValidating());
239         assertEquals(1, pcl.eventCount());
240 
241         fm.setValidating(false);
242         assertTrue(!fm.isValidating());
243         assertEquals(1, pcl.eventCount());
244 
245         fm.setValidating(true);
246         assertTrue(fm.isValidating());
247         assertEquals(2, pcl.eventCount());
248 
249         fm.setEnabled(true);
250         assertTrue(fm.isValidating());
251         assertEquals(2, pcl.eventCount());
252     }
253 
254     public void testReadOnlyRevert() {
255         FormModel fm = getFormModel(new TestBean());
256         fm.getValueModel("readOnly");
257         fm.revert();
258 
259         // no additional asserts, this test should just not throw an exception!
260     }
261 
262     public void testDefaultFormModelFromValueModel() throws Exception {
263         TestBean testBean = new TestBean();
264         ValueModel valueModel = new ValueHolder(testBean);
265         DefaultFormModel model = new DefaultFormModel(valueModel);
266         assertEquals(testBean, model.getFormObject());
267     }
268 
269     private DefaultValidationResults getValidationResults(String message) {
270         DefaultValidationResults res = new DefaultValidationResults();
271         res.addMessage("simpleProperty", Severity.ERROR, message);
272         return res;
273     }
274 
275     private void assertContainsMessage(String message, Set messages) {
276         assertTrue("Set of messages does not contain expected message '" + message + "'", messages
277                 .contains(new DefaultValidationMessage("simpleProperty", Severity.ERROR, message)));
278     }
279 
280     public static class TestValidator implements Validator {
281 
282         public ValidationResults results = new DefaultValidationResults();
283 
284         public int count;
285 
286         public ValidationResults validate(Object object) {
287             count++;
288             return results;
289         }
290     }
291 
292     public class ErrorBean {
293         public RuntimeException errorToThrow = new UnsupportedOperationException();
294 
295         public Object getError() {
296             return null;
297         }
298 
299         public void setError(Object error) {
300             if (errorToThrow != null) {
301                 throw errorToThrow;
302             }
303         }
304     }
305 
306     private static class TestDefaultFormModel extends DefaultFormModel {
307         public TestDefaultFormModel(Object bean) {
308             super(bean, false);
309         }
310 
311         public TestDefaultFormModel(BeanPropertyAccessStrategy pas, boolean buffering) {
312             super(pas, buffering);
313         }
314 
315         public void init() {
316             super.init();
317             setValidator(new TestValidator());
318             setBindingErrorMessageProvider(new BindingErrorMessageProvider() {
319 
320                 public ValidationMessage getErrorMessage(FormModel formModel, String propertyName,
321                         Object valueBeingSet, Exception e) {
322                     return new DefaultValidationMessage(propertyName, Severity.ERROR, "");
323                 }
324 
325             });
326         }
327     }
328 
329     private static class ExceptionConverter implements Converter {
330 
331         private final Class sourceClass;
332 
333         private final Class targetClass;
334 
335         public ExceptionConverter(Class sourceClass, Class targetClass) {
336             this.sourceClass = sourceClass;
337             this.targetClass = targetClass;
338         }
339 
340         public Object convert(Object source, Class targetClass, ConversionContext context) throws ConversionException {
341             throw new ConversionException("test", targetClass);
342         }
343 
344         public Class[] getSourceClasses() {
345             return new Class[] { sourceClass };
346         }
347 
348         public Class[] getTargetClasses() {
349             return new Class[] { targetClass };
350         }
351 
352     }
353 }