View Javadoc
1   /*
2    * Copyright 2002-2015 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of 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,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.beans;
18  
19  import java.beans.PropertyEditorSupport;
20  import java.math.BigDecimal;
21  import java.math.BigInteger;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.LinkedList;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Optional;
32  import java.util.Properties;
33  import java.util.Set;
34  import java.util.SortedMap;
35  import java.util.SortedSet;
36  import java.util.TreeMap;
37  import java.util.TreeSet;
38  
39  import org.apache.commons.logging.LogFactory;
40  import org.junit.Test;
41  
42  import org.springframework.beans.factory.annotation.Autowire;
43  import org.springframework.beans.propertyeditors.CustomNumberEditor;
44  import org.springframework.beans.propertyeditors.StringArrayPropertyEditor;
45  import org.springframework.beans.propertyeditors.StringTrimmerEditor;
46  import org.springframework.beans.support.DerivedFromProtectedBaseBean;
47  import org.springframework.core.convert.ConversionFailedException;
48  import org.springframework.core.convert.TypeDescriptor;
49  import org.springframework.core.convert.support.DefaultConversionService;
50  import org.springframework.core.convert.support.GenericConversionService;
51  import org.springframework.tests.Assume;
52  import org.springframework.tests.TestGroup;
53  import org.springframework.tests.sample.beans.BooleanTestBean;
54  import org.springframework.tests.sample.beans.ITestBean;
55  import org.springframework.tests.sample.beans.IndexedTestBean;
56  import org.springframework.tests.sample.beans.NumberTestBean;
57  import org.springframework.tests.sample.beans.TestBean;
58  import org.springframework.util.StopWatch;
59  import org.springframework.util.StringUtils;
60  
61  import static org.hamcrest.Matchers.*;
62  import static org.junit.Assert.*;
63  
64  /**
65   * @author Rod Johnson
66   * @author Juergen Hoeller
67   * @author Alef Arendsen
68   * @author Arjen Poutsma
69   * @author Chris Beams
70   * @author Dave Syer
71   */
72  public final class BeanWrapperTests extends AbstractConfigurablePropertyAccessorTests {
73  
74  	@Override
75  	protected ConfigurablePropertyAccessor createAccessor(Object target) {
76  		return new BeanWrapperImpl(target);
77  	}
78  
79  	@Test
80  	public void testNullNestedTypeDescriptor() {
81  		Foo foo = new Foo();
82  		BeanWrapperImpl wrapper = new BeanWrapperImpl(foo);
83  		wrapper.setConversionService(new DefaultConversionService());
84  		wrapper.setAutoGrowNestedPaths(true);
85  		wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9");
86  		assertEquals("9", foo.listOfMaps.get(0).get("luckyNumber"));
87  	}
88  
89  	@Test
90  	public void testNullNestedTypeDescriptor2() {
91  		Foo foo = new Foo();
92  		BeanWrapperImpl wrapper = new BeanWrapperImpl(foo);
93  		wrapper.setConversionService(new DefaultConversionService());
94  		wrapper.setAutoGrowNestedPaths(true);
95  		Map<String, String> map = new HashMap<String, String>();
96  		map.put("favoriteNumber", "9");
97  		wrapper.setPropertyValue("list[0]", map);
98  		assertEquals(map, foo.list.get(0));
99  	}
100 
101 	@Test
102 	public void testNullNestedTypeDescriptorWithNoConversionService() {
103 		Foo foo = new Foo();
104 		BeanWrapperImpl wrapper = new BeanWrapperImpl(foo);
105 		wrapper.setAutoGrowNestedPaths(true);
106 		wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9");
107 		assertEquals("9", foo.listOfMaps.get(0).get("luckyNumber"));
108 	}
109 
110 	@Test
111 	public void testNullNestedTypeDescriptorWithBadConversionService() {
112 		Foo foo = new Foo();
113 		BeanWrapperImpl wrapper = new BeanWrapperImpl(foo);
114 		wrapper.setConversionService(new GenericConversionService() {
115 			@Override
116 			public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
117 				throw new ConversionFailedException(sourceType, targetType, source, null);
118 			}
119 		});
120 		wrapper.setAutoGrowNestedPaths(true);
121 		wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9");
122 		assertEquals("9", foo.listOfMaps.get(0).get("luckyNumber"));
123 	}
124 
125 
126 	@Test
127 	public void testReadableAndWritableForIndexedProperties() {
128 		BeanWrapper bw = new BeanWrapperImpl(IndexedTestBean.class);
129 
130 		assertTrue(bw.isReadableProperty("array"));
131 		assertTrue(bw.isReadableProperty("list"));
132 		assertTrue(bw.isReadableProperty("set"));
133 		assertTrue(bw.isReadableProperty("map"));
134 		assertFalse(bw.isReadableProperty("xxx"));
135 
136 		assertTrue(bw.isWritableProperty("array"));
137 		assertTrue(bw.isWritableProperty("list"));
138 		assertTrue(bw.isWritableProperty("set"));
139 		assertTrue(bw.isWritableProperty("map"));
140 		assertFalse(bw.isWritableProperty("xxx"));
141 
142 		assertTrue(bw.isReadableProperty("array[0]"));
143 		assertTrue(bw.isReadableProperty("array[0].name"));
144 		assertTrue(bw.isReadableProperty("list[0]"));
145 		assertTrue(bw.isReadableProperty("list[0].name"));
146 		assertTrue(bw.isReadableProperty("set[0]"));
147 		assertTrue(bw.isReadableProperty("set[0].name"));
148 		assertTrue(bw.isReadableProperty("map[key1]"));
149 		assertTrue(bw.isReadableProperty("map[key1].name"));
150 		assertTrue(bw.isReadableProperty("map[key4][0]"));
151 		assertTrue(bw.isReadableProperty("map[key4][0].name"));
152 		assertTrue(bw.isReadableProperty("map[key4][1]"));
153 		assertTrue(bw.isReadableProperty("map[key4][1].name"));
154 		assertFalse(bw.isReadableProperty("array[key1]"));
155 
156 		assertTrue(bw.isWritableProperty("array[0]"));
157 		assertTrue(bw.isWritableProperty("array[0].name"));
158 		assertTrue(bw.isWritableProperty("list[0]"));
159 		assertTrue(bw.isWritableProperty("list[0].name"));
160 		assertTrue(bw.isWritableProperty("set[0]"));
161 		assertTrue(bw.isWritableProperty("set[0].name"));
162 		assertTrue(bw.isWritableProperty("map[key1]"));
163 		assertTrue(bw.isWritableProperty("map[key1].name"));
164 		assertTrue(bw.isWritableProperty("map[key4][0]"));
165 		assertTrue(bw.isWritableProperty("map[key4][0].name"));
166 		assertTrue(bw.isWritableProperty("map[key4][1]"));
167 		assertTrue(bw.isWritableProperty("map[key4][1].name"));
168 		assertFalse(bw.isWritableProperty("array[key1]"));
169 	}
170 
171 	@Test
172 	public void testTypeDeterminationForIndexedProperty() {
173 		BeanWrapper bw = new BeanWrapperImpl(IndexedTestBean.class);
174 		assertEquals(null, bw.getPropertyType("map[key0]"));
175 
176 		bw = new BeanWrapperImpl(IndexedTestBean.class);
177 		bw.setPropertyValue("map[key0]", "my String");
178 		assertEquals(String.class, bw.getPropertyType("map[key0]"));
179 
180 		bw = new BeanWrapperImpl(IndexedTestBean.class);
181 		bw.registerCustomEditor(String.class, "map[key0]", new StringTrimmerEditor(false));
182 		assertEquals(String.class, bw.getPropertyType("map[key0]"));
183 	}
184 
185 	@Test
186 	public void testGetterThrowsException() {
187 		GetterBean gb = new GetterBean();
188 		BeanWrapper bw = new BeanWrapperImpl(gb);
189 		bw.setPropertyValue("name", "tom");
190 		assertTrue("Set name to tom", gb.getName().equals("tom"));
191 	}
192 
193 	@Test
194 	public void testEmptyPropertyValuesSet() {
195 		TestBean t = new TestBean();
196 		int age = 50;
197 		String name = "Tony";
198 		t.setAge(age);
199 		t.setName(name);
200 		try {
201 			BeanWrapper bw = new BeanWrapperImpl(t);
202 			assertTrue("age is OK", t.getAge() == age);
203 			assertTrue("name is OK", name.equals(t.getName()));
204 			bw.setPropertyValues(new MutablePropertyValues());
205 			// Check its unchanged
206 			assertTrue("age is OK", t.getAge() == age);
207 			assertTrue("name is OK", name.equals(t.getName()));
208 		}
209 		catch (BeansException ex) {
210 			fail("Shouldn't throw exception when everything is valid");
211 		}
212 	}
213 
214 	@Test
215 	public void testAllValid() {
216 		TestBean t = new TestBean();
217 		String newName = "tony";
218 		int newAge = 65;
219 		String newTouchy = "valid";
220 		try {
221 			BeanWrapper bw = new BeanWrapperImpl(t);
222 			MutablePropertyValues pvs = new MutablePropertyValues();
223 			pvs.addPropertyValue(new PropertyValue("age", new Integer(newAge)));
224 			pvs.addPropertyValue(new PropertyValue("name", newName));
225 
226 			pvs.addPropertyValue(new PropertyValue("touchy", newTouchy));
227 			bw.setPropertyValues(pvs);
228 			assertTrue("Validly set property must stick", t.getName().equals(newName));
229 			assertTrue("Validly set property must stick", t.getTouchy().equals(newTouchy));
230 			assertTrue("Validly set property must stick", t.getAge() == newAge);
231 		}
232 		catch (BeansException ex) {
233 			fail("Shouldn't throw exception when everything is valid");
234 		}
235 	}
236 
237 	@Test
238 	public void testBeanWrapperUpdates() {
239 		TestBean t = new TestBean();
240 		int newAge = 33;
241 		try {
242 			BeanWrapper bw = new BeanWrapperImpl(t);
243 			t.setAge(newAge);
244 			Object bwAge = bw.getPropertyValue("age");
245 			assertTrue("Age is an integer", bwAge instanceof Integer);
246 			int bwi = ((Integer) bwAge).intValue();
247 			assertTrue("Bean wrapper must pick up changes", bwi == newAge);
248 		}
249 		catch (Exception ex) {
250 			fail("Shouldn't throw exception when everything is valid");
251 		}
252 	}
253 
254 	@Test
255 	public void testValidNullUpdate() {
256 		TestBean t = new TestBean();
257 		t.setName("Frank");	// we need to change it back
258 		t.setSpouse(t);
259 		BeanWrapper bw = new BeanWrapperImpl(t);
260 		assertTrue("name is not null to start off", t.getName() != null);
261 		bw.setPropertyValue("name", null);
262 		assertTrue("name is now null", t.getName() == null);
263 		// now test with non-string
264 		assertTrue("spouse is not null to start off", t.getSpouse() != null);
265 		bw.setPropertyValue("spouse", null);
266 		assertTrue("spouse is now null", t.getSpouse() == null);
267 	}
268 
269 	@Test
270 	public void testIgnoringIndexedProperty() {
271 		MutablePropertyValues values = new MutablePropertyValues();
272 		values.add("toBeIgnored[0]", new Integer(42));
273 		BeanWrapper bw = new BeanWrapperImpl(new Object());
274 		bw.setPropertyValues(values, true);
275 	}
276 
277 	@Test
278 	public void testConvertPrimitiveToString() {
279 		MutablePropertyValues values = new MutablePropertyValues();
280 		values.add("name", new Integer(42));
281 		TestBean tb = new TestBean();
282 		BeanWrapper bw = new BeanWrapperImpl(tb);
283 		bw.setPropertyValues(values);
284 		assertEquals("42", tb.getName());
285 	}
286 
287 	@Test
288 	public void testConvertClassToString() {
289 		MutablePropertyValues values = new MutablePropertyValues();
290 		values.add("name", Integer.class);
291 		TestBean tb = new TestBean();
292 		BeanWrapper bw = new BeanWrapperImpl(tb);
293 		bw.registerCustomEditor(String.class, new PropertyEditorSupport() {
294 			@Override
295 			public void setValue(Object value) {
296 				super.setValue(value.toString());
297 			}
298 		});
299 		bw.setPropertyValues(values);
300 		assertEquals(Integer.class.toString(), tb.getName());
301 	}
302 
303 	@Test
304 	public void testBooleanObject() {
305 		BooleanTestBean tb = new BooleanTestBean();
306 		BeanWrapper bw = new BeanWrapperImpl(tb);
307 
308 		bw.setPropertyValue("bool2", "true");
309 		assertTrue("Correct bool2 value", Boolean.TRUE.equals(bw.getPropertyValue("bool2")));
310 		assertTrue("Correct bool2 value", tb.getBool2().booleanValue());
311 
312 		bw.setPropertyValue("bool2", "false");
313 		assertTrue("Correct bool2 value", Boolean.FALSE.equals(bw.getPropertyValue("bool2")));
314 		assertTrue("Correct bool2 value", !tb.getBool2().booleanValue());
315 
316 	}
317 
318 	@Test
319 	public void testNumberObjects() {
320 		NumberTestBean tb = new NumberTestBean();
321 		BeanWrapper bw = new BeanWrapperImpl(tb);
322 
323 		try {
324 			bw.setPropertyValue("short2", "2");
325 			bw.setPropertyValue("int2", "8");
326 			bw.setPropertyValue("long2", "6");
327 			bw.setPropertyValue("bigInteger", "3");
328 			bw.setPropertyValue("float2", "8.1");
329 			bw.setPropertyValue("double2", "6.1");
330 			bw.setPropertyValue("bigDecimal", "4.0");
331 		}
332 		catch (BeansException ex) {
333 			fail("Should not throw BeansException: " + ex.getMessage());
334 		}
335 
336 		assertTrue("Correct short2 value", new Short("2").equals(bw.getPropertyValue("short2")));
337 		assertTrue("Correct short2 value", new Short("2").equals(tb.getShort2()));
338 		assertTrue("Correct int2 value", new Integer("8").equals(bw.getPropertyValue("int2")));
339 		assertTrue("Correct int2 value", new Integer("8").equals(tb.getInt2()));
340 		assertTrue("Correct long2 value", new Long("6").equals(bw.getPropertyValue("long2")));
341 		assertTrue("Correct long2 value", new Long("6").equals(tb.getLong2()));
342 		assertTrue("Correct bigInteger value", new BigInteger("3").equals(bw.getPropertyValue("bigInteger")));
343 		assertTrue("Correct bigInteger value", new BigInteger("3").equals(tb.getBigInteger()));
344 		assertTrue("Correct float2 value", new Float("8.1").equals(bw.getPropertyValue("float2")));
345 		assertTrue("Correct float2 value", new Float("8.1").equals(tb.getFloat2()));
346 		assertTrue("Correct double2 value", new Double("6.1").equals(bw.getPropertyValue("double2")));
347 		assertTrue("Correct double2 value", new Double("6.1").equals(tb.getDouble2()));
348 		assertTrue("Correct bigDecimal value", new BigDecimal("4.0").equals(bw.getPropertyValue("bigDecimal")));
349 		assertTrue("Correct bigDecimal value", new BigDecimal("4.0").equals(tb.getBigDecimal()));
350 	}
351 
352 	@Test
353 	public void testNumberCoercion() {
354 		NumberTestBean tb = new NumberTestBean();
355 		BeanWrapper bw = new BeanWrapperImpl(tb);
356 
357 		try {
358 			bw.setPropertyValue("short2", new Integer(2));
359 			bw.setPropertyValue("int2", new Long(8));
360 			bw.setPropertyValue("long2", new BigInteger("6"));
361 			bw.setPropertyValue("bigInteger", new Integer(3));
362 			bw.setPropertyValue("float2", new Double(8.1));
363 			bw.setPropertyValue("double2", new BigDecimal(6.1));
364 			bw.setPropertyValue("bigDecimal", new Float(4.0));
365 		}
366 		catch (BeansException ex) {
367 			fail("Should not throw BeansException: " + ex.getMessage());
368 		}
369 
370 		assertTrue("Correct short2 value", new Short("2").equals(bw.getPropertyValue("short2")));
371 		assertTrue("Correct short2 value", new Short("2").equals(tb.getShort2()));
372 		assertTrue("Correct int2 value", new Integer("8").equals(bw.getPropertyValue("int2")));
373 		assertTrue("Correct int2 value", new Integer("8").equals(tb.getInt2()));
374 		assertTrue("Correct long2 value", new Long("6").equals(bw.getPropertyValue("long2")));
375 		assertTrue("Correct long2 value", new Long("6").equals(tb.getLong2()));
376 		assertTrue("Correct bigInteger value", new BigInteger("3").equals(bw.getPropertyValue("bigInteger")));
377 		assertTrue("Correct bigInteger value", new BigInteger("3").equals(tb.getBigInteger()));
378 		assertTrue("Correct float2 value", new Float("8.1").equals(bw.getPropertyValue("float2")));
379 		assertTrue("Correct float2 value", new Float("8.1").equals(tb.getFloat2()));
380 		assertTrue("Correct double2 value", new Double("6.1").equals(bw.getPropertyValue("double2")));
381 		assertTrue("Correct double2 value", new Double("6.1").equals(tb.getDouble2()));
382 		assertTrue("Correct bigDecimal value", new BigDecimal("4.0").equals(bw.getPropertyValue("bigDecimal")));
383 		assertTrue("Correct bigDecimal value", new BigDecimal("4.0").equals(tb.getBigDecimal()));
384 	}
385 
386 	@Test
387 	public void testEnumByFieldName() {
388 		EnumTester et = new EnumTester();
389 		BeanWrapper bw = new BeanWrapperImpl(et);
390 
391 		bw.setPropertyValue("autowire", "BY_NAME");
392 		assertEquals(Autowire.BY_NAME, et.getAutowire());
393 
394 		bw.setPropertyValue("autowire", "  BY_TYPE ");
395 		assertEquals(Autowire.BY_TYPE, et.getAutowire());
396 
397 		try {
398 			bw.setPropertyValue("autowire", "NHERITED");
399 			fail("Should have thrown TypeMismatchException");
400 		}
401 		catch (TypeMismatchException ex) {
402 			// expected
403 		}
404 	}
405 
406 	@Test
407 	public void testPropertiesProperty() throws Exception {
408 		PropsTester pt = new PropsTester();
409 		BeanWrapper bw = new BeanWrapperImpl(pt);
410 		bw.setPropertyValue("name", "ptest");
411 
412 		// Note format...
413 		String ps = "peace=war\nfreedom=slavery";
414 		bw.setPropertyValue("properties", ps);
415 
416 		assertTrue("name was set", pt.name.equals("ptest"));
417 		assertTrue("props non null", pt.props != null);
418 		String freedomVal = pt.props.getProperty("freedom");
419 		String peaceVal = pt.props.getProperty("peace");
420 		assertTrue("peace==war", peaceVal.equals("war"));
421 		assertTrue("Freedom==slavery", freedomVal.equals("slavery"));
422 	}
423 
424 	@Test
425 	public void testStringArrayProperty() throws Exception {
426 		PropsTester pt = new PropsTester();
427 		BeanWrapper bw = new BeanWrapperImpl(pt);
428 
429 		bw.setPropertyValue("stringArray", new String[] {"foo", "fi", "fi", "fum"});
430 		assertTrue("stringArray length = 4", pt.stringArray.length == 4);
431 		assertTrue("correct values", pt.stringArray[0].equals("foo") && pt.stringArray[1].equals("fi") &&
432 				pt.stringArray[2].equals("fi") && pt.stringArray[3].equals("fum"));
433 
434 		List<String> list = new ArrayList<String>();
435 		list.add("foo");
436 		list.add("fi");
437 		list.add("fi");
438 		list.add("fum");
439 		bw.setPropertyValue("stringArray", list);
440 		assertTrue("stringArray length = 4", pt.stringArray.length == 4);
441 		assertTrue("correct values", pt.stringArray[0].equals("foo") && pt.stringArray[1].equals("fi") &&
442 				pt.stringArray[2].equals("fi") && pt.stringArray[3].equals("fum"));
443 
444 		Set<String> set = new HashSet<String>();
445 		set.add("foo");
446 		set.add("fi");
447 		set.add("fum");
448 		bw.setPropertyValue("stringArray", set);
449 		assertTrue("stringArray length = 3", pt.stringArray.length == 3);
450 		List<String> result = Arrays.asList(pt.stringArray);
451 		assertTrue("correct values", result.contains("foo") && result.contains("fi") && result.contains("fum"));
452 
453 		bw.setPropertyValue("stringArray", "one");
454 		assertTrue("stringArray length = 1", pt.stringArray.length == 1);
455 		assertTrue("stringArray elt is ok", pt.stringArray[0].equals("one"));
456 
457 		bw.setPropertyValue("stringArray", null);
458 		assertTrue("stringArray is null", pt.stringArray == null);
459 	}
460 
461 	@Test
462 	public void testStringArrayPropertyWithCustomStringEditor() throws Exception {
463 		PropsTester pt = new PropsTester();
464 		BeanWrapper bw = new BeanWrapperImpl(pt);
465 		bw.registerCustomEditor(String.class, "stringArray", new PropertyEditorSupport() {
466 			@Override
467 			public void setAsText(String text) {
468 				setValue(text.substring(1));
469 			}
470 		});
471 
472 		bw.setPropertyValue("stringArray", new String[] {"4foo", "7fi", "6fi", "5fum"});
473 		assertTrue("stringArray length = 4", pt.stringArray.length == 4);
474 		assertTrue("correct values", pt.stringArray[0].equals("foo") && pt.stringArray[1].equals("fi") &&
475 				pt.stringArray[2].equals("fi") && pt.stringArray[3].equals("fum"));
476 
477 		List<String> list = new ArrayList<String>();
478 		list.add("4foo");
479 		list.add("7fi");
480 		list.add("6fi");
481 		list.add("5fum");
482 		bw.setPropertyValue("stringArray", list);
483 		assertTrue("stringArray length = 4", pt.stringArray.length == 4);
484 		assertTrue("correct values", pt.stringArray[0].equals("foo") && pt.stringArray[1].equals("fi") &&
485 				pt.stringArray[2].equals("fi") && pt.stringArray[3].equals("fum"));
486 
487 		Set<String> set = new HashSet<String>();
488 		set.add("4foo");
489 		set.add("7fi");
490 		set.add("6fum");
491 		bw.setPropertyValue("stringArray", set);
492 		assertTrue("stringArray length = 3", pt.stringArray.length == 3);
493 		List<String> result = Arrays.asList(pt.stringArray);
494 		assertTrue("correct values", result.contains("foo") && result.contains("fi") && result.contains("fum"));
495 
496 		bw.setPropertyValue("stringArray", "8one");
497 		assertTrue("stringArray length = 1", pt.stringArray.length == 1);
498 		assertTrue("correct values", pt.stringArray[0].equals("one"));
499 	}
500 
501 	@Test
502 	public void testStringArrayPropertyWithStringSplitting() throws Exception {
503 		PropsTester pt = new PropsTester();
504 		BeanWrapperImpl bw = new BeanWrapperImpl(pt);
505 		bw.useConfigValueEditors();
506 		bw.setPropertyValue("stringArray", "a1,b2");
507 		assertTrue("stringArray length = 2", pt.stringArray.length == 2);
508 		assertTrue("correct values", pt.stringArray[0].equals("a1") && pt.stringArray[1].equals("b2"));
509 	}
510 
511 	@Test
512 	public void testStringArrayPropertyWithCustomStringDelimiter() throws Exception {
513 		PropsTester pt = new PropsTester();
514 		BeanWrapper bw = new BeanWrapperImpl(pt);
515 		bw.registerCustomEditor(String[].class, "stringArray", new StringArrayPropertyEditor("-"));
516 		bw.setPropertyValue("stringArray", "a1-b2");
517 		assertTrue("stringArray length = 2", pt.stringArray.length == 2);
518 		assertTrue("correct values", pt.stringArray[0].equals("a1") && pt.stringArray[1].equals("b2"));
519 	}
520 
521 	@Test
522 	public void testStringArrayAutoGrow() throws Exception {
523 		StringArrayBean target = new StringArrayBean();
524 		BeanWrapper bw = new BeanWrapperImpl(target);
525 		bw.setAutoGrowNestedPaths(true);
526 
527 		bw.setPropertyValue("array[0]", "Test0");
528 		assertEquals(1, target.getArray().length);
529 
530 		bw.setPropertyValue("array[2]", "Test2");
531 		assertEquals(3, target.getArray().length);
532 		assertTrue("correct values", target.getArray()[0].equals("Test0") && target.getArray()[1] == null &&
533 				target.getArray()[2].equals("Test2"));
534 	}
535 
536 	@Test
537 	public void testPrimitiveArrayAutoGrow() throws Exception {
538 		PrimitiveArrayBean target = new PrimitiveArrayBean();
539 		BeanWrapper bw = new BeanWrapperImpl(target);
540 		bw.setAutoGrowNestedPaths(true);
541 
542 		bw.setPropertyValue("array[0]", 1);
543 		assertEquals(1, target.getArray().length);
544 
545 		bw.setPropertyValue("array[2]", 3);
546 		assertEquals(3, target.getArray().length);
547 		assertTrue("correct values", target.getArray()[0] == 1 && target.getArray()[1] == 0 &&
548 				target.getArray()[2] == 3);
549 	}
550 
551 	@Test
552 	public void testStringPropertyWithCustomEditor() throws Exception {
553 		TestBean tb = new TestBean();
554 		BeanWrapper bw = new BeanWrapperImpl(tb);
555 		bw.registerCustomEditor(String.class, "name", new PropertyEditorSupport() {
556 			@Override
557 			public void setValue(Object value) {
558 				if (value instanceof String[]) {
559 					setValue(StringUtils.arrayToDelimitedString(((String[]) value), "-"));
560 				}
561 				else {
562 					super.setValue(value != null ? value : "");
563 				}
564 			}
565 		});
566 		bw.setPropertyValue("name", new String[] {});
567 		assertEquals("", tb.getName());
568 		bw.setPropertyValue("name", new String[] {"a1", "b2"});
569 		assertEquals("a1-b2", tb.getName());
570 		bw.setPropertyValue("name", null);
571 		assertEquals("", tb.getName());
572 	}
573 
574 	@Test
575 	public void testIntArrayProperty() {
576 		PropsTester pt = new PropsTester();
577 		BeanWrapper bw = new BeanWrapperImpl(pt);
578 
579 		bw.setPropertyValue("intArray", new int[] {4, 5, 2, 3});
580 		assertTrue("intArray length = 4", pt.intArray.length == 4);
581 		assertTrue("correct values", pt.intArray[0] == 4 && pt.intArray[1] == 5 &&
582 				pt.intArray[2] == 2 && pt.intArray[3] == 3);
583 
584 		bw.setPropertyValue("intArray", new String[] {"4", "5", "2", "3"});
585 		assertTrue("intArray length = 4", pt.intArray.length == 4);
586 		assertTrue("correct values", pt.intArray[0] == 4 && pt.intArray[1] == 5 &&
587 				pt.intArray[2] == 2 && pt.intArray[3] == 3);
588 
589 		List<Object> list = new ArrayList<Object>();
590 		list.add(new Integer(4));
591 		list.add("5");
592 		list.add(new Integer(2));
593 		list.add("3");
594 		bw.setPropertyValue("intArray", list);
595 		assertTrue("intArray length = 4", pt.intArray.length == 4);
596 		assertTrue("correct values", pt.intArray[0] == 4 && pt.intArray[1] == 5 &&
597 				pt.intArray[2] == 2 && pt.intArray[3] == 3);
598 
599 		Set<Object> set = new HashSet<Object>();
600 		set.add("4");
601 		set.add(new Integer(5));
602 		set.add("3");
603 		bw.setPropertyValue("intArray", set);
604 		assertTrue("intArray length = 3", pt.intArray.length == 3);
605 		List<Integer> result = new ArrayList<Integer>();
606 		result.add(new Integer(pt.intArray[0]));
607 		result.add(new Integer(pt.intArray[1]));
608 		result.add(new Integer(pt.intArray[2]));
609 		assertTrue("correct values", result.contains(new Integer(4)) && result.contains(new Integer(5)) &&
610 				result.contains(new Integer(3)));
611 
612 		bw.setPropertyValue("intArray", new Integer[] {new Integer(1)});
613 		assertTrue("intArray length = 4", pt.intArray.length == 1);
614 		assertTrue("correct values", pt.intArray[0] == 1);
615 
616 		bw.setPropertyValue("intArray", new Integer(1));
617 		assertTrue("intArray length = 4", pt.intArray.length == 1);
618 		assertTrue("correct values", pt.intArray[0] == 1);
619 
620 		bw.setPropertyValue("intArray", new String[] {"1"});
621 		assertTrue("intArray length = 4", pt.intArray.length == 1);
622 		assertTrue("correct values", pt.intArray[0] == 1);
623 
624 		bw.setPropertyValue("intArray", "1");
625 		assertTrue("intArray length = 4", pt.intArray.length == 1);
626 		assertTrue("correct values", pt.intArray[0] == 1);
627 	}
628 
629 	@Test
630 	public void testIntArrayPropertyWithCustomEditor() {
631 		PropsTester pt = new PropsTester();
632 		BeanWrapper bw = new BeanWrapperImpl(pt);
633 		bw.registerCustomEditor(int.class, new PropertyEditorSupport() {
634 			@Override
635 			public void setAsText(String text) {
636 				setValue(new Integer(Integer.parseInt(text) + 1));
637 			}
638 		});
639 
640 		bw.setPropertyValue("intArray", new int[] {4, 5, 2, 3});
641 		assertTrue("intArray length = 4", pt.intArray.length == 4);
642 		assertTrue("correct values", pt.intArray[0] == 4 && pt.intArray[1] == 5 &&
643 				pt.intArray[2] == 2 && pt.intArray[3] == 3);
644 
645 		bw.setPropertyValue("intArray", new String[] {"3", "4", "1", "2"});
646 		assertTrue("intArray length = 4", pt.intArray.length == 4);
647 		assertTrue("correct values", pt.intArray[0] == 4 && pt.intArray[1] == 5 &&
648 				pt.intArray[2] == 2 && pt.intArray[3] == 3);
649 
650 		bw.setPropertyValue("intArray", new Integer(1));
651 		assertTrue("intArray length = 4", pt.intArray.length == 1);
652 		assertTrue("correct values", pt.intArray[0] == 1);
653 
654 		bw.setPropertyValue("intArray", new String[] {"0"});
655 		assertTrue("intArray length = 4", pt.intArray.length == 1);
656 		assertTrue("correct values", pt.intArray[0] == 1);
657 
658 		bw.setPropertyValue("intArray", "0");
659 		assertTrue("intArray length = 4", pt.intArray.length == 1);
660 		assertTrue("correct values", pt.intArray[0] == 1);
661 	}
662 
663 	@Test
664 	public void testIntArrayPropertyWithStringSplitting() throws Exception {
665 		PropsTester pt = new PropsTester();
666 		BeanWrapperImpl bw = new BeanWrapperImpl(pt);
667 		bw.useConfigValueEditors();
668 		bw.setPropertyValue("intArray", "4,5");
669 		assertTrue("intArray length = 2", pt.intArray.length == 2);
670 		assertTrue("correct values", pt.intArray[0] == 4 && pt.intArray[1] == 5);
671 	}
672 
673 	@Test
674 	public void testIndividualAllValid() {
675 		TestBean t = new TestBean();
676 		String newName = "tony";
677 		int newAge = 65;
678 		String newTouchy = "valid";
679 		try {
680 			BeanWrapper bw = new BeanWrapperImpl(t);
681 			bw.setPropertyValue("age", new Integer(newAge));
682 			bw.setPropertyValue(new PropertyValue("name", newName));
683 			bw.setPropertyValue(new PropertyValue("touchy", newTouchy));
684 			assertTrue("Validly set property must stick", t.getName().equals(newName));
685 			assertTrue("Validly set property must stick", t.getTouchy().equals(newTouchy));
686 			assertTrue("Validly set property must stick", t.getAge() == newAge);
687 		}
688 		catch (BeansException ex) {
689 			fail("Shouldn't throw exception when everything is valid");
690 		}
691 	}
692 
693 	@Test
694 	public void test2Invalid() {
695 		TestBean t = new TestBean();
696 		String newName = "tony";
697 		String invalidTouchy = ".valid";
698 		try {
699 			BeanWrapper bw = new BeanWrapperImpl(t);
700 			MutablePropertyValues pvs = new MutablePropertyValues();
701 			pvs.addPropertyValue(new PropertyValue("age", "foobar"));
702 			pvs.addPropertyValue(new PropertyValue("name", newName));
703 			pvs.addPropertyValue(new PropertyValue("touchy", invalidTouchy));
704 			bw.setPropertyValues(pvs);
705 			fail("Should throw exception when everything is valid");
706 		}
707 		catch (PropertyBatchUpdateException ex) {
708 			assertTrue("Must contain 2 exceptions", ex.getExceptionCount() == 2);
709 			// Test validly set property matches
710 			assertTrue("Validly set property must stick", t.getName().equals(newName));
711 			assertTrue("Invalidly set property must retain old value", t.getAge() == 0);
712 			assertTrue("New value of dodgy setter must be available through exception",
713 					ex.getPropertyAccessException("touchy").getPropertyChangeEvent().getNewValue().equals(invalidTouchy));
714 		}
715 	}
716 
717 	@Test
718 	public void testPossibleMatches() {
719 		TestBean tb = new TestBean();
720 		try {
721 			BeanWrapper bw = new BeanWrapperImpl(tb);
722 			bw.setPropertyValue("ag", "foobar");
723 			fail("Should throw exception on invalid property");
724 		}
725 		catch (NotWritablePropertyException ex) {
726 			// expected
727 			assertEquals(1, ex.getPossibleMatches().length);
728 			assertEquals("age", ex.getPossibleMatches()[0]);
729 		}
730 	}
731 
732 	@Test
733 	public void testTypeMismatch() {
734 		TestBean t = new TestBean();
735 		try {
736 			BeanWrapper bw = new BeanWrapperImpl(t);
737 			bw.setPropertyValue("age", "foobar");
738 			fail("Should throw exception on type mismatch");
739 		}
740 		catch (TypeMismatchException ex) {
741 			// expected
742 		}
743 	}
744 
745 	@Test
746 	public void testEmptyValueForPrimitiveProperty() {
747 		TestBean t = new TestBean();
748 		try {
749 			BeanWrapper bw = new BeanWrapperImpl(t);
750 			bw.setPropertyValue("age", "");
751 			fail("Should throw exception on type mismatch");
752 		}
753 		catch (TypeMismatchException ex) {
754 			// expected
755 		}
756 		catch (Exception ex) {
757 			fail("Shouldn't throw exception other than Type mismatch");
758 		}
759 	}
760 
761 	@Test
762 	public void testSetPropertyValuesIgnoresInvalidNestedOnRequest() {
763 		ITestBean rod = new TestBean();
764 		MutablePropertyValues pvs = new MutablePropertyValues();
765 		pvs.addPropertyValue(new PropertyValue("name", "rod"));
766 		pvs.addPropertyValue(new PropertyValue("graceful.rubbish", "tony"));
767 		pvs.addPropertyValue(new PropertyValue("more.garbage", new Object()));
768 		BeanWrapper bw = new BeanWrapperImpl(rod);
769 		bw.setPropertyValues(pvs, true);
770 		assertTrue("Set valid and ignored invalid", rod.getName().equals("rod"));
771 		try {
772 			// Don't ignore: should fail
773 			bw.setPropertyValues(pvs, false);
774 			fail("Shouldn't have ignored invalid updates");
775 		}
776 		catch (NotWritablePropertyException ex) {
777 			// OK: but which exception??
778 		}
779 	}
780 
781 	@Test
782 	public void testGetNestedProperty() {
783 		ITestBean rod = new TestBean("rod", 31);
784 		ITestBean kerry = new TestBean("kerry", 35);
785 		rod.setSpouse(kerry);
786 		kerry.setSpouse(rod);
787 		BeanWrapper bw = new BeanWrapperImpl(rod);
788 		Integer KA = (Integer) bw.getPropertyValue("spouse.age");
789 		assertTrue("kerry is 35", KA.intValue() == 35);
790 		Integer RA = (Integer) bw.getPropertyValue("spouse.spouse.age");
791 		assertTrue("rod is 31, not" + RA, RA.intValue() == 31);
792 		ITestBean spousesSpouse = (ITestBean) bw.getPropertyValue("spouse.spouse");
793 		assertTrue("spousesSpouse = initial point", rod == spousesSpouse);
794 	}
795 
796 	@Test
797 	public void testGetNestedPropertyNullValue() throws Exception {
798 		ITestBean rod = new TestBean("rod", 31);
799 		ITestBean kerry = new TestBean("kerry", 35);
800 		rod.setSpouse(kerry);
801 
802 		BeanWrapper bw = new BeanWrapperImpl(rod);
803 		try {
804 			bw.getPropertyValue("spouse.spouse.age");
805 			fail("Shouldn't have succeded with null path");
806 		}
807 		catch (NullValueInNestedPathException ex) {
808 			// ok
809 			assertTrue("it was the spouse.spouse property that was null, not " + ex.getPropertyName(),
810 					ex.getPropertyName().equals("spouse.spouse"));
811 		}
812 	}
813 
814 	@Test
815 	public void testSetNestedProperty() throws Exception {
816 		ITestBean rod = new TestBean("rod", 31);
817 		ITestBean kerry = new TestBean("kerry", 0);
818 
819 		BeanWrapper bw = new BeanWrapperImpl(rod);
820 		bw.setPropertyValue("spouse", kerry);
821 
822 		assertTrue("nested set worked", rod.getSpouse() == kerry);
823 		assertTrue("no back relation", kerry.getSpouse() == null);
824 		bw.setPropertyValue(new PropertyValue("spouse.spouse", rod));
825 		assertTrue("nested set worked", kerry.getSpouse() == rod);
826 		assertTrue("kerry age not set", kerry.getAge() == 0);
827 		bw.setPropertyValue(new PropertyValue("spouse.age", new Integer(35)));
828 		assertTrue("Set primitive on spouse", kerry.getAge() == 35);
829 
830 		assertEquals(kerry, bw.getPropertyValue("spouse"));
831 		assertEquals(rod, bw.getPropertyValue("spouse.spouse"));
832 	}
833 
834 	@Test
835 	public void testSetNestedPropertyNullValue() throws Exception {
836 		ITestBean rod = new TestBean("rod", 31);
837 		BeanWrapper bw = new BeanWrapperImpl(rod);
838 		try {
839 			bw.setPropertyValue("spouse.age", new Integer(31));
840 			fail("Shouldn't have succeeded with null path");
841 		}
842 		catch (NullValueInNestedPathException ex) {
843 			// expected
844 			assertTrue("it was the spouse property that was null, not " + ex.getPropertyName(),
845 					ex.getPropertyName().equals("spouse"));
846 		}
847 	}
848 
849 	@Test
850 	public void testSetNestedPropertyPolymorphic() throws Exception {
851 		ITestBean rod = new TestBean("rod", 31);
852 		ITestBean kerry = new Employee();
853 
854 		BeanWrapper bw = new BeanWrapperImpl(rod);
855 		bw.setPropertyValue("spouse", kerry);
856 		bw.setPropertyValue("spouse.age", new Integer(35));
857 		bw.setPropertyValue("spouse.name", "Kerry");
858 		bw.setPropertyValue("spouse.company", "Lewisham");
859 		assertTrue("kerry name is Kerry", kerry.getName().equals("Kerry"));
860 
861 		assertTrue("nested set worked", rod.getSpouse() == kerry);
862 		assertTrue("no back relation", kerry.getSpouse() == null);
863 		bw.setPropertyValue(new PropertyValue("spouse.spouse", rod));
864 		assertTrue("nested set worked", kerry.getSpouse() == rod);
865 
866 		BeanWrapper kbw = new BeanWrapperImpl(kerry);
867 		assertTrue("spouse.spouse.spouse.spouse.company=Lewisham",
868 				"Lewisham".equals(kbw.getPropertyValue("spouse.spouse.spouse.spouse.company")));
869 	}
870 
871 	@Test
872 	public void testNullObject() {
873 		try {
874 			new BeanWrapperImpl((Object) null);
875 			fail("Must throw an exception when constructed with null object");
876 		}
877 		catch (IllegalArgumentException ex) {
878 			// expected
879 		}
880 	}
881 
882 	@Test
883 	public void testNestedProperties() {
884 		String doctorCompany = "";
885 		String lawyerCompany = "Dr. Sueem";
886 		TestBean tb = new TestBean();
887 		BeanWrapper bw = new BeanWrapperImpl(tb);
888 		bw.setPropertyValue("doctor.company", doctorCompany);
889 		bw.setPropertyValue("lawyer.company", lawyerCompany);
890 		assertEquals(doctorCompany, tb.getDoctor().getCompany());
891 		assertEquals(lawyerCompany, tb.getLawyer().getCompany());
892 	}
893 
894 	@Test
895 	public void testIndexedProperties() {
896 		IndexedTestBean bean = new IndexedTestBean();
897 		BeanWrapper bw = new BeanWrapperImpl(bean);
898 		TestBean tb0 = bean.getArray()[0];
899 		TestBean tb1 = bean.getArray()[1];
900 		TestBean tb2 = ((TestBean) bean.getList().get(0));
901 		TestBean tb3 = ((TestBean) bean.getList().get(1));
902 		TestBean tb6 = ((TestBean) bean.getSet().toArray()[0]);
903 		TestBean tb7 = ((TestBean) bean.getSet().toArray()[1]);
904 		TestBean tb4 = ((TestBean) bean.getMap().get("key1"));
905 		TestBean tb5 = ((TestBean) bean.getMap().get("key.3"));
906 		assertEquals("name0", tb0.getName());
907 		assertEquals("name1", tb1.getName());
908 		assertEquals("name2", tb2.getName());
909 		assertEquals("name3", tb3.getName());
910 		assertEquals("name6", tb6.getName());
911 		assertEquals("name7", tb7.getName());
912 		assertEquals("name4", tb4.getName());
913 		assertEquals("name5", tb5.getName());
914 		assertEquals("name0", bw.getPropertyValue("array[0].name"));
915 		assertEquals("name1", bw.getPropertyValue("array[1].name"));
916 		assertEquals("name2", bw.getPropertyValue("list[0].name"));
917 		assertEquals("name3", bw.getPropertyValue("list[1].name"));
918 		assertEquals("name6", bw.getPropertyValue("set[0].name"));
919 		assertEquals("name7", bw.getPropertyValue("set[1].name"));
920 		assertEquals("name4", bw.getPropertyValue("map[key1].name"));
921 		assertEquals("name5", bw.getPropertyValue("map[key.3].name"));
922 		assertEquals("name4", bw.getPropertyValue("map['key1'].name"));
923 		assertEquals("name5", bw.getPropertyValue("map[\"key.3\"].name"));
924 		assertEquals("nameX", bw.getPropertyValue("map[key4][0].name"));
925 		assertEquals("nameY", bw.getPropertyValue("map[key4][1].name"));
926 
927 		MutablePropertyValues pvs = new MutablePropertyValues();
928 		pvs.add("array[0].name", "name5");
929 		pvs.add("array[1].name", "name4");
930 		pvs.add("list[0].name", "name3");
931 		pvs.add("list[1].name", "name2");
932 		pvs.add("set[0].name", "name8");
933 		pvs.add("set[1].name", "name9");
934 		pvs.add("map[key1].name", "name1");
935 		pvs.add("map['key.3'].name", "name0");
936 		pvs.add("map[key4][0].name", "nameA");
937 		pvs.add("map[key4][1].name", "nameB");
938 		bw.setPropertyValues(pvs);
939 		assertEquals("name5", tb0.getName());
940 		assertEquals("name4", tb1.getName());
941 		assertEquals("name3", tb2.getName());
942 		assertEquals("name2", tb3.getName());
943 		assertEquals("name1", tb4.getName());
944 		assertEquals("name0", tb5.getName());
945 		assertEquals("name5", bw.getPropertyValue("array[0].name"));
946 		assertEquals("name4", bw.getPropertyValue("array[1].name"));
947 		assertEquals("name3", bw.getPropertyValue("list[0].name"));
948 		assertEquals("name2", bw.getPropertyValue("list[1].name"));
949 		assertEquals("name8", bw.getPropertyValue("set[0].name"));
950 		assertEquals("name9", bw.getPropertyValue("set[1].name"));
951 		assertEquals("name1", bw.getPropertyValue("map[\"key1\"].name"));
952 		assertEquals("name0", bw.getPropertyValue("map['key.3'].name"));
953 		assertEquals("nameA", bw.getPropertyValue("map[key4][0].name"));
954 		assertEquals("nameB", bw.getPropertyValue("map[key4][1].name"));
955 	}
956 
957 	@Test
958 	public void testIndexedPropertiesWithDirectAccess() {
959 		IndexedTestBean bean = new IndexedTestBean();
960 		BeanWrapper bw = new BeanWrapperImpl(bean);
961 		TestBean tb0 = bean.getArray()[0];
962 		TestBean tb1 = bean.getArray()[1];
963 		TestBean tb2 = ((TestBean) bean.getList().get(0));
964 		TestBean tb3 = ((TestBean) bean.getList().get(1));
965 		TestBean tb6 = ((TestBean) bean.getSet().toArray()[0]);
966 		TestBean tb7 = ((TestBean) bean.getSet().toArray()[1]);
967 		TestBean tb4 = ((TestBean) bean.getMap().get("key1"));
968 		TestBean tb5 = ((TestBean) bean.getMap().get("key2"));
969 		assertEquals(tb0, bw.getPropertyValue("array[0]"));
970 		assertEquals(tb1, bw.getPropertyValue("array[1]"));
971 		assertEquals(tb2, bw.getPropertyValue("list[0]"));
972 		assertEquals(tb3, bw.getPropertyValue("list[1]"));
973 		assertEquals(tb6, bw.getPropertyValue("set[0]"));
974 		assertEquals(tb7, bw.getPropertyValue("set[1]"));
975 		assertEquals(tb4, bw.getPropertyValue("map[key1]"));
976 		assertEquals(tb5, bw.getPropertyValue("map[key2]"));
977 		assertEquals(tb4, bw.getPropertyValue("map['key1']"));
978 		assertEquals(tb5, bw.getPropertyValue("map[\"key2\"]"));
979 
980 		MutablePropertyValues pvs = new MutablePropertyValues();
981 		pvs.add("array[0]", tb5);
982 		pvs.add("array[1]", tb4);
983 		pvs.add("list[0]", tb3);
984 		pvs.add("list[1]", tb2);
985 		pvs.add("list[2]", tb0);
986 		pvs.add("list[4]", tb1);
987 		pvs.add("map[key1]", tb1);
988 		pvs.add("map['key2']", tb0);
989 		pvs.add("map[key5]", tb4);
990 		pvs.add("map['key9']", tb5);
991 		bw.setPropertyValues(pvs);
992 		assertEquals(tb5, bean.getArray()[0]);
993 		assertEquals(tb4, bean.getArray()[1]);
994 		assertEquals(tb3, (bean.getList().get(0)));
995 		assertEquals(tb2, (bean.getList().get(1)));
996 		assertEquals(tb0, (bean.getList().get(2)));
997 		assertEquals(null, (bean.getList().get(3)));
998 		assertEquals(tb1, (bean.getList().get(4)));
999 		assertEquals(tb1, (bean.getMap().get("key1")));
1000 		assertEquals(tb0, (bean.getMap().get("key2")));
1001 		assertEquals(tb4, (bean.getMap().get("key5")));
1002 		assertEquals(tb5, (bean.getMap().get("key9")));
1003 		assertEquals(tb5, bw.getPropertyValue("array[0]"));
1004 		assertEquals(tb4, bw.getPropertyValue("array[1]"));
1005 		assertEquals(tb3, bw.getPropertyValue("list[0]"));
1006 		assertEquals(tb2, bw.getPropertyValue("list[1]"));
1007 		assertEquals(tb0, bw.getPropertyValue("list[2]"));
1008 		assertEquals(null, bw.getPropertyValue("list[3]"));
1009 		assertEquals(tb1, bw.getPropertyValue("list[4]"));
1010 		assertEquals(tb1, bw.getPropertyValue("map[\"key1\"]"));
1011 		assertEquals(tb0, bw.getPropertyValue("map['key2']"));
1012 		assertEquals(tb4, bw.getPropertyValue("map[\"key5\"]"));
1013 		assertEquals(tb5, bw.getPropertyValue("map['key9']"));
1014 	}
1015 
1016 	@Test
1017 	public void testMapAccessWithTypeConversion() {
1018 		IndexedTestBean bean = new IndexedTestBean();
1019 		BeanWrapper bw = new BeanWrapperImpl(bean);
1020 		bw.registerCustomEditor(TestBean.class, new PropertyEditorSupport() {
1021 			@Override
1022 			public void setAsText(String text) throws IllegalArgumentException {
1023 				if (!StringUtils.hasLength(text)) {
1024 					throw new IllegalArgumentException();
1025 				}
1026 				setValue(new TestBean(text));
1027 			}
1028 		});
1029 
1030 		MutablePropertyValues pvs = new MutablePropertyValues();
1031 		pvs.add("map[key1]", "rod");
1032 		pvs.add("map[key2]", "rob");
1033 		bw.setPropertyValues(pvs);
1034 		assertEquals("rod", ((TestBean) bean.getMap().get("key1")).getName());
1035 		assertEquals("rob", ((TestBean) bean.getMap().get("key2")).getName());
1036 
1037 		pvs = new MutablePropertyValues();
1038 		pvs.add("map[key1]", "rod");
1039 		pvs.add("map[key2]", "");
1040 		try {
1041 			bw.setPropertyValues(pvs);
1042 			fail("Should have thrown TypeMismatchException");
1043 		}
1044 		catch (PropertyBatchUpdateException ex) {
1045 			PropertyAccessException pae = ex.getPropertyAccessException("map[key2]");
1046 			assertTrue(pae instanceof TypeMismatchException);
1047 		}
1048 	}
1049 
1050 	@Test
1051 	public void testMapAccessWithUnmodifiableMap() {
1052 		IndexedTestBean bean = new IndexedTestBean();
1053 		BeanWrapper bw = new BeanWrapperImpl(bean);
1054 		bw.registerCustomEditor(TestBean.class, "map", new PropertyEditorSupport() {
1055 			@Override
1056 			public void setAsText(String text) throws IllegalArgumentException {
1057 				if (!StringUtils.hasLength(text)) {
1058 					throw new IllegalArgumentException();
1059 				}
1060 				setValue(new TestBean(text));
1061 			}
1062 		});
1063 
1064 		Map<Integer, String> inputMap = new HashMap<Integer, String>();
1065 		inputMap.put(new Integer(1), "rod");
1066 		inputMap.put(new Integer(2), "rob");
1067 		MutablePropertyValues pvs = new MutablePropertyValues();
1068 		pvs.add("map", Collections.unmodifiableMap(inputMap));
1069 		bw.setPropertyValues(pvs);
1070 		assertEquals("rod", ((TestBean) bean.getMap().get(new Integer(1))).getName());
1071 		assertEquals("rob", ((TestBean) bean.getMap().get(new Integer(2))).getName());
1072 	}
1073 
1074 	@Test
1075 	public void testMapAccessWithCustomUnmodifiableMap() {
1076 		IndexedTestBean bean = new IndexedTestBean();
1077 		BeanWrapper bw = new BeanWrapperImpl(bean);
1078 		bw.registerCustomEditor(TestBean.class, "map", new PropertyEditorSupport() {
1079 			@Override
1080 			public void setAsText(String text) throws IllegalArgumentException {
1081 				if (!StringUtils.hasLength(text)) {
1082 					throw new IllegalArgumentException();
1083 				}
1084 				setValue(new TestBean(text));
1085 			}
1086 		});
1087 
1088 		Map<Object, Object> inputMap = new HashMap<Object, Object>();
1089 		inputMap.put(new Integer(1), "rod");
1090 		inputMap.put(new Integer(2), "rob");
1091 		MutablePropertyValues pvs = new MutablePropertyValues();
1092 		pvs.add("map", new ReadOnlyMap(inputMap));
1093 		bw.setPropertyValues(pvs);
1094 		assertEquals("rod", ((TestBean) bean.getMap().get(new Integer(1))).getName());
1095 		assertEquals("rob", ((TestBean) bean.getMap().get(new Integer(2))).getName());
1096 	}
1097 
1098 	@SuppressWarnings("unchecked") // must work with raw map in this test
1099 	@Test
1100 	public void testRawMapAccessWithNoEditorRegistered() {
1101 		IndexedTestBean bean = new IndexedTestBean();
1102 		BeanWrapper bw = new BeanWrapperImpl(bean);
1103 		Map inputMap = new HashMap();
1104 		inputMap.put(new Integer(1), "rod");
1105 		inputMap.put(new Integer(2), "rob");
1106 		ReadOnlyMap readOnlyMap = new ReadOnlyMap(inputMap);
1107 		MutablePropertyValues pvs = new MutablePropertyValues();
1108 		pvs.add("map", readOnlyMap);
1109 		bw.setPropertyValues(pvs);
1110 		assertSame(readOnlyMap, bean.getMap());
1111 		assertFalse(readOnlyMap.isAccessed());
1112 	}
1113 
1114 	@Test
1115 	public void testTypedMapReadOnlyMap() {
1116 		TypedReadOnlyMap map = new TypedReadOnlyMap(Collections.singletonMap("key", new TestBean()));
1117 		TypedReadOnlyMapClient bean = new TypedReadOnlyMapClient();
1118 		BeanWrapper bw = new BeanWrapperImpl(bean);
1119 		bw.setPropertyValue("map", map);
1120 	}
1121 
1122 	@Test
1123 	public void testPrimitiveArray() {
1124 		PrimitiveArrayBean tb = new PrimitiveArrayBean();
1125 		BeanWrapper bw = new BeanWrapperImpl(tb);
1126 		bw.setPropertyValue("array", new String[] {"1", "2"});
1127 		assertEquals(2, tb.getArray().length);
1128 		assertEquals(1, tb.getArray()[0]);
1129 		assertEquals(2, tb.getArray()[1]);
1130 	}
1131 
1132 	@Test
1133 	public void testLargeMatchingPrimitiveArray() {
1134 		Assume.group(TestGroup.PERFORMANCE);
1135 		Assume.notLogging(LogFactory.getLog(BeanWrapperTests.class));
1136 
1137 		PrimitiveArrayBean tb = new PrimitiveArrayBean();
1138 		BeanWrapper bw = new BeanWrapperImpl(tb);
1139 		int[] input = new int[1024];
1140 		StopWatch sw = new StopWatch();
1141 		sw.start("array1");
1142 		for (int i = 0; i < 1000; i++) {
1143 			bw.setPropertyValue("array", input);
1144 		}
1145 		sw.stop();
1146 		assertEquals(1024, tb.getArray().length);
1147 		assertEquals(0, tb.getArray()[0]);
1148 		long time1 = sw.getLastTaskTimeMillis();
1149 		assertTrue("Took too long", sw.getLastTaskTimeMillis() < 100);
1150 
1151 		bw.registerCustomEditor(String.class, new StringTrimmerEditor(false));
1152 		sw.start("array2");
1153 		for (int i = 0; i < 1000; i++) {
1154 			bw.setPropertyValue("array", input);
1155 		}
1156 		sw.stop();
1157 		assertTrue("Took too long", sw.getLastTaskTimeMillis() < 125);
1158 
1159 		bw.registerCustomEditor(int.class, "array.somePath", new CustomNumberEditor(Integer.class, false));
1160 		sw.start("array3");
1161 		for (int i = 0; i < 1000; i++) {
1162 			bw.setPropertyValue("array", input);
1163 		}
1164 		sw.stop();
1165 		assertTrue("Took too long", sw.getLastTaskTimeMillis() < 100);
1166 
1167 		bw.registerCustomEditor(int.class, "array[0].somePath", new CustomNumberEditor(Integer.class, false));
1168 		sw.start("array3");
1169 		for (int i = 0; i < 1000; i++) {
1170 			bw.setPropertyValue("array", input);
1171 		}
1172 		sw.stop();
1173 		assertTrue("Took too long", sw.getLastTaskTimeMillis() < 100);
1174 
1175 		bw.registerCustomEditor(int.class, new CustomNumberEditor(Integer.class, false));
1176 		sw.start("array4");
1177 		for (int i = 0; i < 100; i++) {
1178 			bw.setPropertyValue("array", input);
1179 		}
1180 		sw.stop();
1181 		assertEquals(1024, tb.getArray().length);
1182 		assertEquals(0, tb.getArray()[0]);
1183 		assertTrue("Took too long", sw.getLastTaskTimeMillis() > time1);
1184 	}
1185 
1186 	@Test
1187 	public void testLargeMatchingPrimitiveArrayWithSpecificEditor() {
1188 		PrimitiveArrayBean tb = new PrimitiveArrayBean();
1189 		BeanWrapper bw = new BeanWrapperImpl(tb);
1190 		bw.registerCustomEditor(int.class, "array", new PropertyEditorSupport() {
1191 			@Override
1192 			public void setValue(Object value) {
1193 				if (value instanceof Integer) {
1194 					super.setValue(new Integer(((Integer) value).intValue() + 1));
1195 				}
1196 			}
1197 		});
1198 		int[] input = new int[1024];
1199 		bw.setPropertyValue("array", input);
1200 		assertEquals(1024, tb.getArray().length);
1201 		assertEquals(1, tb.getArray()[0]);
1202 		assertEquals(1, tb.getArray()[1]);
1203 	}
1204 
1205 	@Test
1206 	public void testLargeMatchingPrimitiveArrayWithIndexSpecificEditor() {
1207 		PrimitiveArrayBean tb = new PrimitiveArrayBean();
1208 		BeanWrapper bw = new BeanWrapperImpl(tb);
1209 		bw.registerCustomEditor(int.class, "array[1]", new PropertyEditorSupport() {
1210 			@Override
1211 			public void setValue(Object value) {
1212 				if (value instanceof Integer) {
1213 					super.setValue(new Integer(((Integer) value).intValue() + 1));
1214 				}
1215 			}
1216 		});
1217 		int[] input = new int[1024];
1218 		bw.setPropertyValue("array", input);
1219 		assertEquals(1024, tb.getArray().length);
1220 		assertEquals(0, tb.getArray()[0]);
1221 		assertEquals(1, tb.getArray()[1]);
1222 	}
1223 
1224 	@Test
1225 	public void testPropertiesInProtectedBaseBean() {
1226 		DerivedFromProtectedBaseBean bean = new DerivedFromProtectedBaseBean();
1227 		BeanWrapper bw = new BeanWrapperImpl(bean);
1228 		bw.setPropertyValue("someProperty", "someValue");
1229 		assertEquals("someValue", bw.getPropertyValue("someProperty"));
1230 		assertEquals("someValue", bean.getSomeProperty());
1231 	}
1232 
1233 	@Test
1234 	public void testErrorMessageOfNestedProperty() {
1235 		ITestBean parent = new TestBean();
1236 		ITestBean child = new DifferentTestBean();
1237 		child.setName("test");
1238 		parent.setSpouse(child);
1239 		BeanWrapper bw = new BeanWrapperImpl(parent);
1240 		try {
1241 			bw.getPropertyValue("spouse.bla");
1242 		}
1243 		catch (NotReadablePropertyException ex) {
1244 			assertTrue(ex.getMessage().indexOf(TestBean.class.getName()) != -1);
1245 		}
1246 	}
1247 
1248 	@Test
1249 	public void testMatchingCollections() {
1250 		IndexedTestBean tb = new IndexedTestBean();
1251 		BeanWrapper bw = new BeanWrapperImpl(tb);
1252 		Collection<String> coll = new HashSet<String>();
1253 		coll.add("coll1");
1254 		bw.setPropertyValue("collection", coll);
1255 		Set<String> set = new HashSet<String>();
1256 		set.add("set1");
1257 		bw.setPropertyValue("set", set);
1258 		SortedSet<String> sortedSet = new TreeSet<String>();
1259 		sortedSet.add("sortedSet1");
1260 		bw.setPropertyValue("sortedSet", sortedSet);
1261 		List<String> list = new LinkedList<String>();
1262 		list.add("list1");
1263 		bw.setPropertyValue("list", list);
1264 		assertSame(coll, tb.getCollection());
1265 		assertSame(set, tb.getSet());
1266 		assertSame(sortedSet, tb.getSortedSet());
1267 		assertSame(list, tb.getList());
1268 	}
1269 
1270 	@SuppressWarnings("unchecked") // list cannot be properly parameterized as it breaks other tests
1271 	@Test
1272 	public void testNonMatchingCollections() {
1273 		IndexedTestBean tb = new IndexedTestBean();
1274 		BeanWrapper bw = new BeanWrapperImpl(tb);
1275 		Collection<String> coll = new ArrayList<String>();
1276 		coll.add("coll1");
1277 		bw.setPropertyValue("collection", coll);
1278 		List<String> set = new LinkedList<String>();
1279 		set.add("set1");
1280 		bw.setPropertyValue("set", set);
1281 		List<String> sortedSet = new ArrayList<String>();
1282 		sortedSet.add("sortedSet1");
1283 		bw.setPropertyValue("sortedSet", sortedSet);
1284 		Set<String> list = new HashSet<String>();
1285 		list.add("list1");
1286 		bw.setPropertyValue("list", list);
1287 		assertEquals(1, tb.getCollection().size());
1288 		assertTrue(tb.getCollection().containsAll(coll));
1289 		assertEquals(1, tb.getSet().size());
1290 		assertTrue(tb.getSet().containsAll(set));
1291 		assertEquals(1, tb.getSortedSet().size());
1292 		assertTrue(tb.getSortedSet().containsAll(sortedSet));
1293 		assertEquals(1, tb.getList().size());
1294 		assertTrue(tb.getList().containsAll(list));
1295 	}
1296 
1297 	@SuppressWarnings("unchecked") // list cannot be properly parameterized as it breaks other tests
1298 	@Test
1299 	public void testCollectionsWithArrayValues() {
1300 		IndexedTestBean tb = new IndexedTestBean();
1301 		BeanWrapper bw = new BeanWrapperImpl(tb);
1302 		Collection<String> coll = new HashSet<String>();
1303 		coll.add("coll1");
1304 		bw.setPropertyValue("collection", coll.toArray());
1305 		List<String> set = new LinkedList<String>();
1306 		set.add("set1");
1307 		bw.setPropertyValue("set", set.toArray());
1308 		List<String> sortedSet = new ArrayList<String>();
1309 		sortedSet.add("sortedSet1");
1310 		bw.setPropertyValue("sortedSet", sortedSet.toArray());
1311 		Set<String> list = new HashSet<String>();
1312 		list.add("list1");
1313 		bw.setPropertyValue("list", list.toArray());
1314 		assertEquals(1, tb.getCollection().size());
1315 		assertTrue(tb.getCollection().containsAll(coll));
1316 		assertEquals(1, tb.getSet().size());
1317 		assertTrue(tb.getSet().containsAll(set));
1318 		assertEquals(1, tb.getSortedSet().size());
1319 		assertTrue(tb.getSortedSet().containsAll(sortedSet));
1320 		assertEquals(1, tb.getList().size());
1321 		assertTrue(tb.getList().containsAll(list));
1322 	}
1323 
1324 	@SuppressWarnings("unchecked") // list cannot be properly parameterized as it breaks other tests
1325 	@Test
1326 	public void testCollectionsWithIntArrayValues() {
1327 		IndexedTestBean tb = new IndexedTestBean();
1328 		BeanWrapper bw = new BeanWrapperImpl(tb);
1329 		Collection<Integer> coll = new HashSet<Integer>();
1330 		coll.add(new Integer(0));
1331 		bw.setPropertyValue("collection", new int[] {0});
1332 		List<Integer> set = new LinkedList<Integer>();
1333 		set.add(new Integer(1));
1334 		bw.setPropertyValue("set", new int[] {1});
1335 		List<Integer> sortedSet = new ArrayList<Integer>();
1336 		sortedSet.add(new Integer(2));
1337 		bw.setPropertyValue("sortedSet", new int[] {2});
1338 		Set<Integer> list = new HashSet<Integer>();
1339 		list.add(new Integer(3));
1340 		bw.setPropertyValue("list", new int[] {3});
1341 		assertEquals(1, tb.getCollection().size());
1342 		assertTrue(tb.getCollection().containsAll(coll));
1343 		assertEquals(1, tb.getSet().size());
1344 		assertTrue(tb.getSet().containsAll(set));
1345 		assertEquals(1, tb.getSortedSet().size());
1346 		assertTrue(tb.getSortedSet().containsAll(sortedSet));
1347 		assertEquals(1, tb.getList().size());
1348 		assertTrue(tb.getList().containsAll(list));
1349 	}
1350 
1351 	@SuppressWarnings("unchecked") // list cannot be properly parameterized as it breaks other tests
1352 	@Test
1353 	public void testCollectionsWithIntegerValues() {
1354 		IndexedTestBean tb = new IndexedTestBean();
1355 		BeanWrapper bw = new BeanWrapperImpl(tb);
1356 		Collection<Integer> coll = new HashSet<Integer>();
1357 		coll.add(new Integer(0));
1358 		bw.setPropertyValue("collection", new Integer(0));
1359 		List<Integer> set = new LinkedList<Integer>();
1360 		set.add(new Integer(1));
1361 		bw.setPropertyValue("set", new Integer(1));
1362 		List<Integer> sortedSet = new ArrayList<Integer>();
1363 		sortedSet.add(new Integer(2));
1364 		bw.setPropertyValue("sortedSet", new Integer(2));
1365 		Set<Integer> list = new HashSet<Integer>();
1366 		list.add(new Integer(3));
1367 		bw.setPropertyValue("list", new Integer(3));
1368 		assertEquals(1, tb.getCollection().size());
1369 		assertTrue(tb.getCollection().containsAll(coll));
1370 		assertEquals(1, tb.getSet().size());
1371 		assertTrue(tb.getSet().containsAll(set));
1372 		assertEquals(1, tb.getSortedSet().size());
1373 		assertTrue(tb.getSortedSet().containsAll(sortedSet));
1374 		assertEquals(1, tb.getList().size());
1375 		assertTrue(tb.getList().containsAll(list));
1376 	}
1377 
1378 	@SuppressWarnings("unchecked") // list cannot be properly parameterized as it breaks other tests
1379 	@Test
1380 	public void testCollectionsWithStringValues() {
1381 		IndexedTestBean tb = new IndexedTestBean();
1382 		BeanWrapper bw = new BeanWrapperImpl(tb);
1383 		List<String> set = new LinkedList<String>();
1384 		set.add("set1");
1385 		bw.setPropertyValue("set", "set1");
1386 		List<String> sortedSet = new ArrayList<String>();
1387 		sortedSet.add("sortedSet1");
1388 		bw.setPropertyValue("sortedSet", "sortedSet1");
1389 		Set<String> list = new HashSet<String>();
1390 		list.add("list1");
1391 		bw.setPropertyValue("list", "list1");
1392 		assertEquals(1, tb.getSet().size());
1393 		assertTrue(tb.getSet().containsAll(set));
1394 		assertEquals(1, tb.getSortedSet().size());
1395 		assertTrue(tb.getSortedSet().containsAll(sortedSet));
1396 		assertEquals(1, tb.getList().size());
1397 		assertTrue(tb.getList().containsAll(list));
1398 	}
1399 
1400 	@Test
1401 	public void testCollectionsWithStringValuesAndCustomEditor() {
1402 		IndexedTestBean tb = new IndexedTestBean();
1403 		BeanWrapper bw = new BeanWrapperImpl(tb);
1404 		bw.registerCustomEditor(String.class, "set", new StringTrimmerEditor(false));
1405 		bw.registerCustomEditor(String.class, "list", new StringTrimmerEditor(false));
1406 
1407 		bw.setPropertyValue("set", "set1 ");
1408 		bw.setPropertyValue("sortedSet", "sortedSet1");
1409 		bw.setPropertyValue("list", "list1 ");
1410 		assertEquals(1, tb.getSet().size());
1411 		assertTrue(tb.getSet().contains("set1"));
1412 		assertEquals(1, tb.getSortedSet().size());
1413 		assertTrue(tb.getSortedSet().contains("sortedSet1"));
1414 		assertEquals(1, tb.getList().size());
1415 		assertTrue(tb.getList().contains("list1"));
1416 
1417 		bw.setPropertyValue("list", Arrays.asList(new String[] {"list1 "}));
1418 		assertTrue(tb.getList().contains("list1"));
1419 	}
1420 
1421 	@Test
1422 	public void testMatchingMaps() {
1423 		IndexedTestBean tb = new IndexedTestBean();
1424 		BeanWrapper bw = new BeanWrapperImpl(tb);
1425 		Map<String, String> map = new HashMap<String, String>();
1426 		map.put("key", "value");
1427 		bw.setPropertyValue("map", map);
1428 		SortedMap<?, ?> sortedMap = new TreeMap<Object, Object>();
1429 		map.put("sortedKey", "sortedValue");
1430 		bw.setPropertyValue("sortedMap", sortedMap);
1431 		assertSame(map, tb.getMap());
1432 		assertSame(sortedMap, tb.getSortedMap());
1433 	}
1434 
1435 	@Test
1436 	public void testNonMatchingMaps() {
1437 		IndexedTestBean tb = new IndexedTestBean();
1438 		BeanWrapper bw = new BeanWrapperImpl(tb);
1439 		Map<String, String> map = new TreeMap<String, String>();
1440 		map.put("key", "value");
1441 		bw.setPropertyValue("map", map);
1442 		Map<String, String> sortedMap = new TreeMap<String, String>();
1443 		sortedMap.put("sortedKey", "sortedValue");
1444 		bw.setPropertyValue("sortedMap", sortedMap);
1445 		assertEquals(1, tb.getMap().size());
1446 		assertEquals("value", tb.getMap().get("key"));
1447 		assertEquals(1, tb.getSortedMap().size());
1448 		assertEquals("sortedValue", tb.getSortedMap().get("sortedKey"));
1449 	}
1450 
1451 	@Test
1452 	public void testSetNumberProperties() {
1453 	NumberPropertyBean bean = new NumberPropertyBean();
1454 		BeanWrapper bw = new BeanWrapperImpl(bean);
1455 
1456 		String byteValue = " " + Byte.MAX_VALUE + " ";
1457 		String shortValue = " " + Short.MAX_VALUE + " ";
1458 		String intValue = " " + Integer.MAX_VALUE + " ";
1459 		String longValue = " " + Long.MAX_VALUE + " ";
1460 		String floatValue = " " + Float.MAX_VALUE + " ";
1461 		String doubleValue = " " + Double.MAX_VALUE + " ";
1462 
1463 		bw.setPropertyValue("myPrimitiveByte", byteValue);
1464 		bw.setPropertyValue("myByte", byteValue);
1465 
1466 		bw.setPropertyValue("myPrimitiveShort", shortValue);
1467 		bw.setPropertyValue("myShort", shortValue);
1468 
1469 		bw.setPropertyValue("myPrimitiveInt", intValue);
1470 		bw.setPropertyValue("myInteger", intValue);
1471 
1472 		bw.setPropertyValue("myPrimitiveLong", longValue);
1473 		bw.setPropertyValue("myLong", longValue);
1474 
1475 		bw.setPropertyValue("myPrimitiveFloat", floatValue);
1476 		bw.setPropertyValue("myFloat", floatValue);
1477 
1478 		bw.setPropertyValue("myPrimitiveDouble", doubleValue);
1479 		bw.setPropertyValue("myDouble", doubleValue);
1480 
1481 		assertEquals(Byte.MAX_VALUE, bean.getMyPrimitiveByte());
1482 		assertEquals(Byte.MAX_VALUE, bean.getMyByte().byteValue());
1483 
1484 		assertEquals(Short.MAX_VALUE, bean.getMyPrimitiveShort());
1485 		assertEquals(Short.MAX_VALUE, bean.getMyShort().shortValue());
1486 
1487 		assertEquals(Integer.MAX_VALUE, bean.getMyPrimitiveInt());
1488 		assertEquals(Integer.MAX_VALUE, bean.getMyInteger().intValue());
1489 
1490 		assertEquals(Long.MAX_VALUE, bean.getMyPrimitiveLong());
1491 		assertEquals(Long.MAX_VALUE, bean.getMyLong().longValue());
1492 
1493 		assertEquals(Float.MAX_VALUE, bean.getMyPrimitiveFloat(), 0.001);
1494 		assertEquals(Float.MAX_VALUE, bean.getMyFloat().floatValue(), 0.001);
1495 
1496 		assertEquals(Double.MAX_VALUE, bean.getMyPrimitiveDouble(), 0.001);
1497 		assertEquals(Double.MAX_VALUE, bean.getMyDouble().doubleValue(), 0.001);
1498 
1499 	}
1500 
1501 	@Test
1502 	public void testAlternativesForTypo() {
1503 		IntelliBean ib = new IntelliBean();
1504 		BeanWrapper bw = new BeanWrapperImpl(ib);
1505 		try {
1506 			bw.setPropertyValue("names", "Alef");
1507 		}
1508 		catch (NotWritablePropertyException ex) {
1509 			assertNotNull("Possible matches not determined", ex.getPossibleMatches());
1510 			assertEquals("Invalid amount of alternatives", 1, ex.getPossibleMatches().length);
1511 		}
1512 	}
1513 
1514 	@Test
1515 	public void testAlternativesForTypos() {
1516 		IntelliBean ib = new IntelliBean();
1517 		BeanWrapper bw = new BeanWrapperImpl(ib);
1518 		try {
1519 			bw.setPropertyValue("mystring", "Arjen");
1520 		}
1521 		catch (NotWritablePropertyException ex) {
1522 			assertNotNull("Possible matches not determined", ex.getPossibleMatches());
1523 			assertEquals("Invalid amount of alternatives", 3, ex.getPossibleMatches().length);
1524 		}
1525 	}
1526 
1527 	@Test
1528 	public void testGenericEnum() {
1529 		EnumConsumer consumer = new EnumConsumer();
1530 		BeanWrapper bw = new BeanWrapperImpl(consumer);
1531 		bw.setPropertyValue("enumValue", TestEnum.class.getName() + ".TEST_VALUE");
1532 		assertEquals(TestEnum.TEST_VALUE, consumer.getEnumValue());
1533 	}
1534 
1535 	@Test
1536 	public void testWildcardedGenericEnum() {
1537 		WildcardEnumConsumer consumer = new WildcardEnumConsumer();
1538 		BeanWrapper bw = new BeanWrapperImpl(consumer);
1539 		bw.setPropertyValue("enumValue", TestEnum.class.getName() + ".TEST_VALUE");
1540 		assertEquals(TestEnum.TEST_VALUE, consumer.getEnumValue());
1541 	}
1542 
1543 	@Test
1544 	public void cornerSpr10115() {
1545 		Spr10115Bean foo = new Spr10115Bean();
1546 		BeanWrapperImpl bwi = new BeanWrapperImpl(foo);
1547 		bwi.setPropertyValue("prop1", "val1");
1548 		assertEquals("val1", Spr10115Bean.prop1);
1549 	}
1550 
1551 	@Test
1552 	public void testArrayToObject() {
1553 		ArrayToObject foo = new ArrayToObject();
1554 		BeanWrapperImpl bwi = new BeanWrapperImpl(foo);
1555 
1556 		Object[] array = new Object[] {"1","2"};
1557 		bwi.setPropertyValue("object", array);
1558 		assertThat(foo.getObject(), equalTo((Object) array));
1559 
1560 		array = new Object[] {"1"};
1561 		bwi.setPropertyValue("object", array);
1562 		assertThat(foo.getObject(), equalTo((Object) array));
1563 	}
1564 
1565 	@Test
1566 	public void testPropertyTypeMismatch() {
1567 		PropertyTypeMismatch foo = new PropertyTypeMismatch();
1568 		BeanWrapperImpl bwi = new BeanWrapperImpl(foo);
1569 		bwi.setPropertyValue("object", "a String");
1570 		assertEquals("a String", foo.value);
1571 		assertTrue(foo.getObject() == 8);
1572 		assertEquals(8, bwi.getPropertyValue("object"));
1573 	}
1574 
1575 	@Test
1576 	public void testGetterWithOptional() {
1577 		GetterWithOptional foo = new GetterWithOptional();
1578 		TestBean tb = new TestBean("x");
1579 		BeanWrapperImpl bwi = new BeanWrapperImpl(foo);
1580 
1581 		bwi.setPropertyValue("object", tb);
1582 		assertSame(tb, foo.value);
1583 		assertSame(tb, foo.getObject().get());
1584 		assertSame(tb, ((Optional<String>) bwi.getPropertyValue("object")).get());
1585 		assertEquals("x", foo.value.getName());
1586 		assertEquals("x", foo.getObject().get().getName());
1587 		assertEquals("x", bwi.getPropertyValue("object.name"));
1588 
1589 		bwi.setPropertyValue("object.name", "y");
1590 		assertSame(tb, foo.value);
1591 		assertSame(tb, foo.getObject().get());
1592 		assertSame(tb, ((Optional<String>) bwi.getPropertyValue("object")).get());
1593 		assertEquals("y", foo.value.getName());
1594 		assertEquals("y", foo.getObject().get().getName());
1595 		assertEquals("y", bwi.getPropertyValue("object.name"));
1596 	}
1597 
1598 	@Test
1599 	public void testGetterWithOptionalAndAutoGrowing() {
1600 		GetterWithOptional foo = new GetterWithOptional();
1601 		BeanWrapperImpl bwi = new BeanWrapperImpl(foo);
1602 		bwi.setAutoGrowNestedPaths(true);
1603 
1604 		bwi.setPropertyValue("object.name", "x");
1605 		assertEquals("x", foo.value.getName());
1606 		assertEquals("x", foo.getObject().get().getName());
1607 		assertEquals("x", bwi.getPropertyValue("object.name"));
1608 	}
1609 
1610 	@Test
1611 	public void testGenericArraySetter() {
1612 		SkipReaderStub foo = new SkipReaderStub();
1613 		BeanWrapperImpl bwi = new BeanWrapperImpl(foo);
1614 		List<String> values = new LinkedList<String>();
1615 		values.add("1");
1616 		values.add("2");
1617 		values.add("3");
1618 		values.add("4");
1619 		bwi.setPropertyValue("items", values);
1620 		Object[] result = foo.items;
1621 		assertEquals(4, result.length);
1622 		assertEquals("1", result[0]);
1623 		assertEquals("2", result[1]);
1624 		assertEquals("3", result[2]);
1625 		assertEquals("4", result[3]);
1626 	}
1627 
1628 
1629 	static class Spr10115Bean {
1630 
1631 		private static String prop1;
1632 
1633 		public static void setProp1(String prop1) {
1634 			Spr10115Bean.prop1 = prop1;
1635 		}
1636 	}
1637 
1638 
1639 	@SuppressWarnings("unused")
1640 	private static class Foo {
1641 
1642 		private List list;
1643 
1644 		private List<Map> listOfMaps;
1645 
1646 		public List getList() {
1647 			return list;
1648 		}
1649 
1650 		public void setList(List list) {
1651 			this.list = list;
1652 		}
1653 
1654 		public List<Map> getListOfMaps() {
1655 			return listOfMaps;
1656 		}
1657 
1658 		public void setListOfMaps(List<Map> listOfMaps) {
1659 			this.listOfMaps = listOfMaps;
1660 		}
1661 	}
1662 
1663 
1664 	private static class DifferentTestBean extends TestBean {
1665 		// class to test naming of beans in a BeanWrapper error message
1666 	}
1667 
1668 
1669 
1670 
1671 	@SuppressWarnings("unused")
1672 	private static class EnumTester {
1673 
1674 		private Autowire autowire;
1675 
1676 		public void setAutowire(Autowire autowire) {
1677 			this.autowire = autowire;
1678 		}
1679 
1680 		public Autowire getAutowire() {
1681 			return autowire;
1682 		}
1683 	}
1684 
1685 
1686 	@SuppressWarnings("unused")
1687 	private static class PropsTester {
1688 
1689 		private Properties props;
1690 
1691 		private String name;
1692 
1693 		private String[] stringArray;
1694 
1695 		private int[] intArray;
1696 
1697 		public void setProperties(Properties p) {
1698 			props = p;
1699 		}
1700 
1701 		public void setName(String name) {
1702 			this.name = name;
1703 		}
1704 
1705 		public void setStringArray(String[] sa) {
1706 			this.stringArray = sa;
1707 		}
1708 
1709 		public void setIntArray(int[] intArray) {
1710 			this.intArray = intArray;
1711 		}
1712 	}
1713 
1714 
1715 	@SuppressWarnings("unused")
1716 	private static class GetterBean {
1717 
1718 		private String name;
1719 
1720 		public void setName(String name) {
1721 			this.name = name;
1722 		}
1723 
1724 		public String getName() {
1725 			if (this.name == null) {
1726 				throw new RuntimeException("name property must be set");
1727 			}
1728 			return name;
1729 		}
1730 	}
1731 
1732 
1733 	@SuppressWarnings("unused")
1734 	private static class ThrowsException {
1735 
1736 		public void doSomething(Throwable t) throws Throwable {
1737 			throw t;
1738 		}
1739 	}
1740 
1741 
1742 	@SuppressWarnings("unused")
1743 	private static class PrimitiveArrayBean {
1744 
1745 		private int[] array;
1746 
1747 		public int[] getArray() {
1748 			return array;
1749 		}
1750 
1751 		public void setArray(int[] array) {
1752 			this.array = array;
1753 		}
1754 	}
1755 
1756 	@SuppressWarnings("unused")
1757 	private static class StringArrayBean {
1758 
1759 		private String[] array;
1760 
1761 		public String[] getArray() {
1762 			return array;
1763 		}
1764 
1765 		public void setArray(String[] array) {
1766 			this.array = array;
1767 		}
1768 	}
1769 
1770 
1771 	@SuppressWarnings("unused")
1772 	private static class NumberPropertyBean {
1773 
1774 		private byte myPrimitiveByte;
1775 		private Byte myByte;
1776 
1777 		private short myPrimitiveShort;
1778 		private Short myShort;
1779 
1780 		private int myPrimitiveInt;
1781 		private Integer myInteger;
1782 
1783 		private long myPrimitiveLong;
1784 		private Long myLong;
1785 
1786 		private float myPrimitiveFloat;
1787 		private Float myFloat;
1788 
1789 		private double myPrimitiveDouble;
1790 		private Double myDouble;
1791 
1792 		public byte getMyPrimitiveByte() {
1793 			return myPrimitiveByte;
1794 		}
1795 
1796 		public void setMyPrimitiveByte(byte myPrimitiveByte) {
1797 			this.myPrimitiveByte = myPrimitiveByte;
1798 		}
1799 
1800 		public Byte getMyByte() {
1801 			return myByte;
1802 		}
1803 
1804 		public void setMyByte(Byte myByte) {
1805 			this.myByte = myByte;
1806 		}
1807 
1808 		public short getMyPrimitiveShort() {
1809 			return myPrimitiveShort;
1810 		}
1811 
1812 		public void setMyPrimitiveShort(short myPrimitiveShort) {
1813 			this.myPrimitiveShort = myPrimitiveShort;
1814 		}
1815 
1816 		public Short getMyShort() {
1817 			return myShort;
1818 		}
1819 
1820 		public void setMyShort(Short myShort) {
1821 			this.myShort = myShort;
1822 		}
1823 
1824 		public int getMyPrimitiveInt() {
1825 			return myPrimitiveInt;
1826 		}
1827 
1828 		public void setMyPrimitiveInt(int myPrimitiveInt) {
1829 			this.myPrimitiveInt = myPrimitiveInt;
1830 		}
1831 
1832 		public Integer getMyInteger() {
1833 			return myInteger;
1834 		}
1835 
1836 		public void setMyInteger(Integer myInteger) {
1837 			this.myInteger = myInteger;
1838 		}
1839 
1840 		public long getMyPrimitiveLong() {
1841 			return myPrimitiveLong;
1842 		}
1843 
1844 		public void setMyPrimitiveLong(long myPrimitiveLong) {
1845 			this.myPrimitiveLong = myPrimitiveLong;
1846 		}
1847 
1848 		public Long getMyLong() {
1849 			return myLong;
1850 		}
1851 
1852 		public void setMyLong(Long myLong) {
1853 			this.myLong = myLong;
1854 		}
1855 
1856 		public float getMyPrimitiveFloat() {
1857 			return myPrimitiveFloat;
1858 		}
1859 
1860 		public void setMyPrimitiveFloat(float myPrimitiveFloat) {
1861 			this.myPrimitiveFloat = myPrimitiveFloat;
1862 		}
1863 
1864 		public Float getMyFloat() {
1865 			return myFloat;
1866 		}
1867 
1868 		public void setMyFloat(Float myFloat) {
1869 			this.myFloat = myFloat;
1870 		}
1871 
1872 		public double getMyPrimitiveDouble() {
1873 			return myPrimitiveDouble;
1874 		}
1875 
1876 		public void setMyPrimitiveDouble(double myPrimitiveDouble) {
1877 			this.myPrimitiveDouble = myPrimitiveDouble;
1878 		}
1879 
1880 		public Double getMyDouble() {
1881 			return myDouble;
1882 		}
1883 
1884 		public void setMyDouble(Double myDouble) {
1885 			this.myDouble = myDouble;
1886 		}
1887 	}
1888 
1889 
1890 	@SuppressWarnings("unused")
1891 	private static class IntelliBean {
1892 
1893 		public void setName(String name) {}
1894 
1895 		public void setMyString(String string) {}
1896 
1897 		public void setMyStrings(String string) {}
1898 
1899 		public void setMyStriNg(String string) {}
1900 
1901 		public void setMyStringss(String string) {}
1902 	}
1903 
1904 
1905 	@SuppressWarnings("unused")
1906 	private static class Employee extends TestBean {
1907 
1908 		private String co;
1909 
1910 		public String getCompany() {
1911 			return co;
1912 		}
1913 
1914 		public void setCompany(String co) {
1915 			this.co = co;
1916 		}
1917 	}
1918 
1919 
1920 	@SuppressWarnings("serial")
1921 	public static class ReadOnlyMap<K, V> extends HashMap<K, V> {
1922 
1923 		private boolean frozen = false;
1924 
1925 		private boolean accessed = false;
1926 
1927 		public ReadOnlyMap() {
1928 			this.frozen = true;
1929 		}
1930 
1931 		public ReadOnlyMap(Map<? extends K, ? extends V> map) {
1932 			super(map);
1933 			this.frozen = true;
1934 		}
1935 
1936 		@Override
1937 		public V put(K key, V value) {
1938 			if (this.frozen) {
1939 				throw new UnsupportedOperationException();
1940 			}
1941 			else {
1942 				return super.put(key, value);
1943 			}
1944 		}
1945 
1946 		@Override
1947 		public Set<Map.Entry<K, V>> entrySet() {
1948 			this.accessed = true;
1949 			return super.entrySet();
1950 		}
1951 
1952 		@Override
1953 		public Set<K> keySet() {
1954 			this.accessed = true;
1955 			return super.keySet();
1956 		}
1957 
1958 		@Override
1959 		public int size() {
1960 			this.accessed = true;
1961 			return super.size();
1962 		}
1963 
1964 		public boolean isAccessed() {
1965 			return this.accessed;
1966 		}
1967 	}
1968 
1969 
1970 	@SuppressWarnings("serial")
1971 	public static class TypedReadOnlyMap extends ReadOnlyMap<String, TestBean> {
1972 
1973 		public TypedReadOnlyMap() {
1974 		}
1975 
1976 		public TypedReadOnlyMap(Map<? extends String, ? extends TestBean> map) {
1977 			super(map);
1978 		}
1979 	}
1980 
1981 
1982 	public static class TypedReadOnlyMapClient {
1983 
1984 		public void setMap(TypedReadOnlyMap map) {
1985 		}
1986 	}
1987 
1988 
1989 	public static class EnumConsumer {
1990 
1991 		private Enum<TestEnum> enumValue;
1992 
1993 		public Enum<TestEnum> getEnumValue() {
1994 			return enumValue;
1995 		}
1996 
1997 		public void setEnumValue(Enum<TestEnum> enumValue) {
1998 			this.enumValue = enumValue;
1999 		}
2000 	}
2001 
2002 
2003 	public static class WildcardEnumConsumer {
2004 
2005 		private Enum<?> enumValue;
2006 
2007 		public Enum<?> getEnumValue() {
2008 			return enumValue;
2009 		}
2010 
2011 		public void setEnumValue(Enum<?> enumValue) {
2012 			this.enumValue = enumValue;
2013 		}
2014 	}
2015 
2016 
2017 	public enum TestEnum {
2018 
2019 		TEST_VALUE
2020 	}
2021 
2022 
2023 	public static class ArrayToObject {
2024 
2025 		private Object object;
2026 
2027 		public void setObject(Object object) {
2028 			this.object = object;
2029 		}
2030 
2031 		public Object getObject() {
2032 			return object;
2033 		}
2034 	}
2035 
2036 
2037 	public static class PropertyTypeMismatch {
2038 
2039 		public String value;
2040 
2041 		public void setObject(String object) {
2042 			this.value = object;
2043 		}
2044 
2045 		public Integer getObject() {
2046 			return (this.value != null ? this.value.length() : null);
2047 		}
2048 	}
2049 
2050 
2051 	public static class GetterWithOptional {
2052 
2053 		public TestBean value;
2054 
2055 		public void setObject(TestBean object) {
2056 			this.value = object;
2057 		}
2058 
2059 		public Optional<TestBean> getObject() {
2060 			return Optional.ofNullable(this.value);
2061 		}
2062 	}
2063 
2064 
2065 	public static class SkipReaderStub<T> {
2066 
2067 		public T[] items;
2068 
2069 		public SkipReaderStub() {
2070 		}
2071 
2072 		public SkipReaderStub(T... items) {
2073 			this.items = items;
2074 		}
2075 
2076 		public void setItems(T... items) {
2077 			this.items = items;
2078 		}
2079 	}
2080 
2081 }