1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.binding.support;
17
18 import java.beans.PropertyChangeEvent;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23
24 import org.springframework.beans.InvalidPropertyException;
25 import org.springframework.beans.NotReadablePropertyException;
26 import org.springframework.beans.NotWritablePropertyException;
27 import org.springframework.beans.NullValueInNestedPathException;
28 import org.springframework.binding.MutablePropertyAccessStrategy;
29 import org.springframework.binding.PropertyMetadataAccessStrategy;
30 import org.springframework.binding.value.ValueModel;
31 import org.springframework.rules.closure.Closure;
32 import org.springframework.rules.closure.support.Block;
33 import org.springframework.richclient.test.SpringRichTestCase;
34
35
36
37
38
39
40
41 public abstract class AbstractPropertyAccessStrategyTests extends SpringRichTestCase {
42
43 protected AbstractPropertyAccessStrategy pas;
44
45 protected TestBean testBean;
46
47 protected ValueModel vm;
48
49 protected TestPropertyChangeListener pcl;
50
51 protected void doSetUp() throws Exception {
52 testBean = new TestBean();
53 pas = createPropertyAccessStrategy(testBean);
54 pcl = new TestPropertyChangeListener(ValueModel.VALUE_PROPERTY);
55 }
56
57 protected abstract AbstractPropertyAccessStrategy createPropertyAccessStrategy(Object target);
58
59 protected boolean isStrictNullHandlingEnabled() {
60 return true;
61 }
62
63 public void testSimpleProperty() {
64 vm = pas.getPropertyValueModel("simpleProperty");
65 Block setValueDirectly = new Block() {
66 public void handle(Object newValue) {
67 testBean.setSimpleProperty((String)newValue);
68 }
69 };
70 Closure getValueDirectly = new Closure() {
71 public Object call(Object ignore) {
72 return testBean.getSimpleProperty();
73 }
74 };
75 Object[] valuesToTest = new Object[] {"1", "2", null, "3"};
76
77 testSettingAndGetting(valuesToTest, getValueDirectly, setValueDirectly);
78 }
79
80 public void testNestedProperty() {
81 final TestBean nestedProperty = new TestBean();
82 testBean.setNestedProperty(nestedProperty);
83 vm = pas.getPropertyValueModel("nestedProperty.simpleProperty");
84 Block setValueDirectly = new Block() {
85 public void handle(Object newValue) {
86 nestedProperty.setSimpleProperty((String)newValue);
87 }
88 };
89 Closure getValueDirectly = new Closure() {
90 public Object call(Object ignored) {
91 return nestedProperty.getSimpleProperty();
92 }
93 };
94 Object[] valuesToTest = new Object[] {"1", "2", null, "3"};
95
96 testSettingAndGetting(valuesToTest, getValueDirectly, setValueDirectly);
97 }
98
99 public void testChildPropertyAccessStrategy() {
100 final TestBean nestedProperty = new TestBean();
101 testBean.setNestedProperty(nestedProperty);
102 MutablePropertyAccessStrategy cpas = pas.getPropertyAccessStrategyForPath("nestedProperty");
103
104 assertEquals("Child domainObjectHolder should equal equivalent parent ValueModel",
105 pas.getPropertyValueModel("nestedProperty"), cpas.getDomainObjectHolder());
106
107 vm = cpas.getPropertyValueModel("simpleProperty");
108 assertEquals("Child should return the same ValueModel as parent",
109 pas.getPropertyValueModel("nestedProperty.simpleProperty"), vm);
110
111 Block setValueDirectly = new Block() {
112 public void handle(Object newValue) {
113 nestedProperty.setSimpleProperty((String)newValue);
114 }
115 };
116 Closure getValueDirectly = new Closure() {
117 public Object call(Object ignore) {
118 return nestedProperty.getSimpleProperty();
119 }
120 };
121 Object[] valuesToTest = new Object[] {"1", "2", null, "3"};
122
123 testSettingAndGetting(valuesToTest, getValueDirectly, setValueDirectly);
124
125 try {
126 pas.getPropertyValueModel("nestedProperty").setValue(null);
127 if (isStrictNullHandlingEnabled())
128 fail("Should have thrown a NullValueInNestedPathException");
129 }
130 catch (NullValueInNestedPathException e) {
131 if (!isStrictNullHandlingEnabled())
132 fail("Should not have thrown a NullValueInNestedPathException");
133 }
134 }
135
136 public void testMapProperty() {
137 final Map map = new HashMap();
138 testBean.setMapProperty(map);
139 vm = pas.getPropertyValueModel("mapProperty[.key]");
140 Block setValueDirectly = new Block() {
141 public void handle(Object newValue) {
142 map.put(".key", newValue);
143 }
144 };
145 Closure getValueDirectly = new Closure() {
146 public Object call(Object ignore) {
147 return map.get(".key");
148 }
149 };
150 Object[] valuesToTest = new Object[] {"1", "2", null, "3"};
151 testSettingAndGetting(valuesToTest, getValueDirectly, setValueDirectly);
152
153 try {
154 pas.getPropertyValueModel("mapProperty").setValue(null);
155 if (isStrictNullHandlingEnabled())
156 fail("Should have thrown a NullValueInNestedPathException");
157 }
158 catch (NullValueInNestedPathException e) {
159 if (!isStrictNullHandlingEnabled())
160 fail("Should not have thrown a NullValueInNestedPathException");
161 }
162 }
163
164 public void testListProperty() {
165 final List list = new ArrayList();
166 list.add(null);
167 testBean.setListProperty(list);
168 vm = pas.getPropertyValueModel("listProperty[0]");
169
170 Block setValueDirectly = new Block() {
171 public void handle(Object newValue) {
172 list.set(0, newValue);
173 }
174 };
175 Closure getValueDirectly = new Closure() {
176 public Object call(Object ignore) {
177 return list.get(0);
178 }
179 };
180 Object[] valuesToTest = new Object[] {"1", "2", null, "3"};
181 testSettingAndGetting(valuesToTest, getValueDirectly, setValueDirectly);
182
183 list.add("a");
184 ValueModel vm2 = pas.getPropertyValueModel("listProperty[1]");
185 assertEquals("a", vm2.getValue());
186
187 try {
188 List newList = new ArrayList();
189 pas.getPropertyValueModel("listProperty").setValue(newList);
190 if (isStrictNullHandlingEnabled())
191 fail("Should have thrown an InvalidPropertyException");
192 }
193 catch (InvalidPropertyException e) {
194 if (!isStrictNullHandlingEnabled())
195 fail("Should not have thrown an InvalidPropertyException");
196 }
197
198 try {
199 pas.getPropertyValueModel("listProperty").setValue(null);
200 if (isStrictNullHandlingEnabled())
201 fail("Should have thrown a NullValueInNestedPathException");
202 }
203 catch (NullValueInNestedPathException e) {
204 if (!isStrictNullHandlingEnabled())
205 fail("Should not have thrown a NullValueInNestedPathException");
206 }
207 }
208
209 public void testReadOnlyProperty() {
210 vm = pas.getPropertyValueModel("readOnly");
211
212 testBean.readOnly = "1";
213 assertEquals(testBean.readOnly, vm.getValue());
214
215 try {
216 vm.setValue("2");
217 fail("should have thrown NotWritablePropertyException");
218 } catch(NotWritablePropertyException e) {
219
220 }
221 }
222
223 public void testWriteOnlyProperty() {
224 vm = pas.getPropertyValueModel("writeOnly");
225
226 vm.setValue("2");
227 assertEquals("2" , testBean.writeOnly);
228
229 try {
230 vm.getValue();
231 fail("should have thrown NotReadablePropertyException");
232 } catch(NotReadablePropertyException e) {
233
234 }
235 }
236
237 public void testBeanThatImplementsPropertyChangePublisher() {
238 TestBeanWithPCP testBeanPCP = new TestBeanWithPCP();
239 pas.getDomainObjectHolder().setValue(testBeanPCP);
240
241 vm = pas.getPropertyValueModel("boundProperty");
242 assertEquals("ValueModel should have registered a PropertyChangeListener", 1,
243 testBeanPCP.getPropertyChangeListeners("boundProperty").length);
244
245 vm.addValueChangeListener(pcl);
246 testBeanPCP.setBoundProperty("1");
247 assertEquals("Change to bound property should have been detected by ValueModel", 1, pcl.getEventsRecevied()
248 .size());
249 PropertyChangeEvent e = (PropertyChangeEvent)pcl.getEventsRecevied().get(0);
250 assertEquals(vm, e.getSource());
251 assertEquals(null, e.getOldValue());
252 assertEquals("1", e.getNewValue());
253
254 pcl.reset();
255 vm.setValue("2");
256 assertEquals("Change to bound property should have been detected by ValueModel", 1, pcl.getEventsRecevied().size());
257 e = (PropertyChangeEvent)pcl.getEventsRecevied().get(0);
258 assertEquals(vm, e.getSource());
259 assertEquals("1", e.getOldValue());
260 assertEquals("2", e.getNewValue());
261
262 TestBeanWithPCP testBeanPCP2 = new TestBeanWithPCP();
263 pas.getDomainObjectHolder().setValue(testBeanPCP2);
264
265 assertEquals("ValueModel should have removed the PropertyChangeListener", 0,
266 testBeanPCP.getPropertyChangeListeners("boundProperty").length);
267 assertEquals("ValueModel should have registered a PropertyChangeListener", 1,
268 testBeanPCP2.getPropertyChangeListeners("boundProperty").length);
269 }
270
271 private void testSettingAndGetting(Object[] valuesToTest, Closure getValueDirectly, Block setValueDirectly) {
272 vm.addValueChangeListener(pcl);
273 for (int i = 0; i < valuesToTest.length; i++) {
274 final Object valueToTest = valuesToTest[i];
275 pcl.reset();
276 assertEquals("ValueModel does not have same value as bean property", getValueDirectly.call(null), vm.getValue());
277 setValueDirectly.call(valueToTest);
278 assertEquals("Change to bean not picked up by ValueModel", valueToTest, vm.getValue());
279 setValueDirectly.call(null);
280 assertEquals("Change to bean not picked up by ValueModel", null, vm.getValue());
281 vm.setValue(valueToTest);
282 assertEquals("Change to ValueModel not reflected in bean", valueToTest, getValueDirectly.call(null));
283 assertEquals("Change to ValueModel had no effect", valueToTest, vm.getValue());
284 if (valueToTest != null) {
285 assertEquals("Incorrect number of property change events fired by value model",
286 1, pcl.getEventsRecevied().size());
287 PropertyChangeEvent e = (PropertyChangeEvent)pcl.getEventsRecevied().get(0);
288 assertEquals(vm, e.getSource());
289 assertEquals(null, e.getOldValue());
290 assertEquals(valueToTest, e.getNewValue());
291 }
292 }
293 }
294
295 protected void assertPropertyMetadata(PropertyMetadataAccessStrategy mas, String property, Class type, boolean isReadable, boolean isWriteable) {
296 assertEquals(type, mas.getPropertyType(property));
297 assertEquals(isReadable, mas.isReadable(property));
298 assertEquals(isWriteable, mas.isWriteable(property));
299 }
300 }