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.core.convert.support;
18  
19  import java.awt.Color;
20  import java.lang.reflect.Method;
21  import java.math.BigDecimal;
22  import java.math.BigInteger;
23  import java.time.ZoneId;
24  import java.util.AbstractList;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.LinkedHashMap;
31  import java.util.LinkedHashSet;
32  import java.util.LinkedList;
33  import java.util.List;
34  import java.util.Locale;
35  import java.util.Map;
36  import java.util.Optional;
37  import java.util.Properties;
38  import java.util.Set;
39  
40  import org.junit.Test;
41  
42  import org.springframework.core.MethodParameter;
43  import org.springframework.core.convert.ConversionFailedException;
44  import org.springframework.core.convert.ConverterNotFoundException;
45  import org.springframework.core.convert.TypeDescriptor;
46  import org.springframework.core.convert.converter.Converter;
47  import org.springframework.core.convert.converter.ConverterRegistry;
48  import org.springframework.util.ClassUtils;
49  
50  import static org.hamcrest.Matchers.*;
51  import static org.junit.Assert.*;
52  
53  /**
54   * @author Keith Donald
55   * @author Juergen Hoeller
56   */
57  public class DefaultConversionTests {
58  
59  	private final DefaultConversionService conversionService = new DefaultConversionService();
60  
61  
62  	@Test
63  	public void testStringToCharacter() {
64  		assertEquals(Character.valueOf('1'), conversionService.convert("1", Character.class));
65  	}
66  
67  	@Test
68  	public void testStringToCharacterEmptyString() {
69  		assertEquals(null, conversionService.convert("", Character.class));
70  	}
71  
72  	@Test(expected = ConversionFailedException.class)
73  	public void testStringToCharacterInvalidString() {
74  		conversionService.convert("invalid", Character.class);
75  	}
76  
77  	@Test
78  	public void testCharacterToString() {
79  		assertEquals("3", conversionService.convert('3', String.class));
80  	}
81  
82  	@Test
83  	public void testStringToBooleanTrue() {
84  		assertEquals(Boolean.valueOf(true), conversionService.convert("true", Boolean.class));
85  		assertEquals(Boolean.valueOf(true), conversionService.convert("on", Boolean.class));
86  		assertEquals(Boolean.valueOf(true), conversionService.convert("yes", Boolean.class));
87  		assertEquals(Boolean.valueOf(true), conversionService.convert("1", Boolean.class));
88  		assertEquals(Boolean.valueOf(true), conversionService.convert("TRUE", Boolean.class));
89  		assertEquals(Boolean.valueOf(true), conversionService.convert("ON", Boolean.class));
90  		assertEquals(Boolean.valueOf(true), conversionService.convert("YES", Boolean.class));
91  	}
92  
93  	@Test
94  	public void testStringToBooleanFalse() {
95  		assertEquals(Boolean.valueOf(false), conversionService.convert("false", Boolean.class));
96  		assertEquals(Boolean.valueOf(false), conversionService.convert("off", Boolean.class));
97  		assertEquals(Boolean.valueOf(false), conversionService.convert("no", Boolean.class));
98  		assertEquals(Boolean.valueOf(false), conversionService.convert("0", Boolean.class));
99  		assertEquals(Boolean.valueOf(false), conversionService.convert("FALSE", Boolean.class));
100 		assertEquals(Boolean.valueOf(false), conversionService.convert("OFF", Boolean.class));
101 		assertEquals(Boolean.valueOf(false), conversionService.convert("NO", Boolean.class));
102 	}
103 
104 	@Test
105 	public void testStringToBooleanEmptyString() {
106 		assertEquals(null, conversionService.convert("", Boolean.class));
107 	}
108 
109 	@Test(expected = ConversionFailedException.class)
110 	public void testStringToBooleanInvalidString() {
111 		conversionService.convert("invalid", Boolean.class);
112 	}
113 
114 	@Test
115 	public void testBooleanToString() {
116 		assertEquals("true", conversionService.convert(true, String.class));
117 	}
118 
119 	@Test
120 	public void testStringToByte() throws Exception {
121 		assertEquals(Byte.valueOf("1"), conversionService.convert("1", Byte.class));
122 	}
123 
124 	@Test
125 	public void testByteToString() {
126 		assertEquals("65", conversionService.convert(new String("A").getBytes()[0], String.class));
127 	}
128 
129 	@Test
130 	public void testStringToShort() {
131 		assertEquals(Short.valueOf("1"), conversionService.convert("1", Short.class));
132 	}
133 
134 	@Test
135 	public void testShortToString() {
136 		short three = 3;
137 		assertEquals("3", conversionService.convert(three, String.class));
138 	}
139 
140 	@Test
141 	public void testStringToInteger() {
142 		assertEquals(Integer.valueOf("1"), conversionService.convert("1", Integer.class));
143 	}
144 
145 	@Test
146 	public void testIntegerToString() {
147 		assertEquals("3", conversionService.convert(3, String.class));
148 	}
149 
150 	@Test
151 	public void testStringToLong() {
152 		assertEquals(Long.valueOf("1"), conversionService.convert("1", Long.class));
153 	}
154 
155 	@Test
156 	public void testLongToString() {
157 		assertEquals("3", conversionService.convert(3L, String.class));
158 	}
159 
160 	@Test
161 	public void testStringToFloat() {
162 		assertEquals(Float.valueOf("1.0"), conversionService.convert("1.0", Float.class));
163 	}
164 
165 	@Test
166 	public void testFloatToString() {
167 		assertEquals("1.0", conversionService.convert(new Float("1.0"), String.class));
168 	}
169 
170 	@Test
171 	public void testStringToDouble() {
172 		assertEquals(Double.valueOf("1.0"), conversionService.convert("1.0", Double.class));
173 	}
174 
175 	@Test
176 	public void testDoubleToString() {
177 		assertEquals("1.0", conversionService.convert(new Double("1.0"), String.class));
178 	}
179 
180 	@Test
181 	public void testStringToBigInteger() {
182 		assertEquals(new BigInteger("1"), conversionService.convert("1", BigInteger.class));
183 	}
184 
185 	@Test
186 	public void testBigIntegerToString() {
187 		assertEquals("100", conversionService.convert(new BigInteger("100"), String.class));
188 	}
189 
190 	@Test
191 	public void testStringToBigDecimal() {
192 		assertEquals(new BigDecimal("1.0"), conversionService.convert("1.0", BigDecimal.class));
193 	}
194 
195 	@Test
196 	public void testBigDecimalToString() {
197 		assertEquals("100.00", conversionService.convert(new BigDecimal("100.00"), String.class));
198 	}
199 
200 	@Test
201 	public void testStringToNumber() {
202 		assertEquals(new BigDecimal("1.0"), conversionService.convert("1.0", Number.class));
203 	}
204 
205 	@Test
206 	public void testStringToNumberEmptyString() {
207 		assertEquals(null, conversionService.convert("", Number.class));
208 	}
209 
210 	@Test
211 	public void testStringToEnum() throws Exception {
212 		assertEquals(Foo.BAR, conversionService.convert("BAR", Foo.class));
213 	}
214 
215 	@Test
216 	public void testStringToEnumWithSubclass() throws Exception {
217 		assertEquals(SubFoo.BAZ, conversionService.convert("BAZ", SubFoo.BAR.getClass()));
218 	}
219 
220 	@Test
221 	public void testStringToEnumEmptyString() {
222 		assertEquals(null, conversionService.convert("", Foo.class));
223 	}
224 
225 	@Test
226 	public void testEnumToString() {
227 		assertEquals("BAR", conversionService.convert(Foo.BAR, String.class));
228 	}
229 
230 	public static enum Foo {
231 		BAR, BAZ
232 	}
233 
234 	public static enum SubFoo {
235 
236 		BAR {
237 			@Override
238 			String s() {
239 				return "x";
240 			}
241 		},
242 		BAZ {
243 			@Override
244 			String s() {
245 				return "y";
246 			}
247 		};
248 
249 		abstract String s();
250 	}
251 
252 	@Test
253 	public void testStringToLocale() {
254 		assertEquals(Locale.ENGLISH, conversionService.convert("en", Locale.class));
255 	}
256 
257 	@Test
258 	public void testStringToString() {
259 		String str = "test";
260 		assertSame(str, conversionService.convert(str, String.class));
261 	}
262 
263 	@Test
264 	public void testNumberToNumber() {
265 		assertEquals(Long.valueOf(1), conversionService.convert(Integer.valueOf(1), Long.class));
266 	}
267 
268 	@Test(expected = ConversionFailedException.class)
269 	public void testNumberToNumberNotSupportedNumber() {
270 		conversionService.convert(Integer.valueOf(1), CustomNumber.class);
271 	}
272 
273 	@Test
274 	public void testNumberToCharacter() {
275 		assertEquals(Character.valueOf('A'), conversionService.convert(Integer.valueOf(65), Character.class));
276 	}
277 
278 	@Test
279 	public void testCharacterToNumber() {
280 		assertEquals(new Integer(65), conversionService.convert('A', Integer.class));
281 	}
282 
283 	// collection conversion
284 
285 	@Test
286 	public void convertArrayToCollectionInterface() {
287 		List<?> result = conversionService.convert(new String[] { "1", "2", "3" }, List.class);
288 		assertEquals("1", result.get(0));
289 		assertEquals("2", result.get(1));
290 		assertEquals("3", result.get(2));
291 	}
292 
293 	public List<Integer> genericList = new ArrayList<Integer>();
294 
295 	@Test
296 	public void convertArrayToCollectionGenericTypeConversion() throws Exception {
297 		List<Integer> result = (List<Integer>) conversionService.convert(new String[] { "1", "2", "3" }, TypeDescriptor
298 				.valueOf(String[].class), new TypeDescriptor(getClass().getDeclaredField("genericList")));
299 		assertEquals(new Integer("1"), result.get(0));
300 		assertEquals(new Integer("2"), result.get(1));
301 		assertEquals(new Integer("3"), result.get(2));
302 	}
303 
304 	@Test
305 	public void testSpr7766() throws Exception {
306 		ConverterRegistry registry = (conversionService);
307 		registry.addConverter(new ColorConverter());
308 		List<Color> colors = (List<Color>) conversionService.convert(new String[] { "ffffff", "#000000" }, TypeDescriptor.valueOf(String[].class), new TypeDescriptor(new MethodParameter(getClass().getMethod("handlerMethod", List.class), 0)));
309 		assertEquals(2, colors.size());
310 		assertEquals(Color.WHITE, colors.get(0));
311 		assertEquals(Color.BLACK, colors.get(1));
312 	}
313 
314 	public class ColorConverter implements Converter<String, Color> {
315 		@Override
316 		public Color convert(String source) { if (!source.startsWith("#")) source = "#" + source; return Color.decode(source); }
317 	}
318 
319 	public void handlerMethod(List<Color> color) {
320 
321 	}
322 
323 	@Test
324 	public void convertArrayToCollectionImpl() {
325 		LinkedList<?> result = conversionService.convert(new String[] { "1", "2", "3" }, LinkedList.class);
326 		assertEquals("1", result.get(0));
327 		assertEquals("2", result.get(1));
328 		assertEquals("3", result.get(2));
329 	}
330 
331 	@Test(expected = ConversionFailedException.class)
332 	public void convertArrayToAbstractCollection() {
333 		conversionService.convert(new String[] { "1", "2", "3" }, AbstractList.class);
334 	}
335 
336 	public static enum FooEnum {
337 		BAR, BAZ
338 	}
339 
340 	@Test
341 	public void convertArrayToString() {
342 		String result = conversionService.convert(new String[] { "1", "2", "3" }, String.class);
343 		assertEquals("1,2,3", result);
344 	}
345 
346 	@Test
347 	public void convertArrayToStringWithElementConversion() {
348 		String result = conversionService.convert(new Integer[] { 1, 2, 3 }, String.class);
349 		assertEquals("1,2,3", result);
350 	}
351 
352 	@Test
353 	public void convertEmptyArrayToString() {
354 		String result = conversionService.convert(new String[0], String.class);
355 		assertEquals("", result);
356 	}
357 
358 	@Test
359 	public void convertStringToArray() {
360 		String[] result = conversionService.convert("1,2,3", String[].class);
361 		assertEquals(3, result.length);
362 		assertEquals("1", result[0]);
363 		assertEquals("2", result[1]);
364 		assertEquals("3", result[2]);
365 	}
366 
367 	@Test
368 	public void convertStringToArrayWithElementConversion() {
369 		Integer[] result = conversionService.convert("1,2,3", Integer[].class);
370 		assertEquals(3, result.length);
371 		assertEquals(new Integer(1), result[0]);
372 		assertEquals(new Integer(2), result[1]);
373 		assertEquals(new Integer(3), result[2]);
374 	}
375 
376 	@Test
377 	public void convertStringToPrimitiveArrayWithElementConversion() {
378 		int[] result = conversionService.convert("1,2,3", int[].class);
379 		assertEquals(3, result.length);
380 		assertEquals(1, result[0]);
381 		assertEquals(2, result[1]);
382 		assertEquals(3, result[2]);
383 	}
384 
385 	@Test
386 	public void convertEmptyStringToArray() {
387 		String[] result = conversionService.convert("", String[].class);
388 		assertEquals(0, result.length);
389 	}
390 
391 	@Test
392 	public void convertArrayToObject() {
393 		Object[] array = new Object[] { 3L };
394 		Object result = conversionService.convert(array, Long.class);
395 		assertEquals(3L, result);
396 	}
397 
398 	@Test
399 	public void convertArrayToObjectWithElementConversion() {
400 		String[] array = new String[] { "3" };
401 		Integer result = conversionService.convert(array, Integer.class);
402 		assertEquals(new Integer(3), result);
403 	}
404 
405 	@Test
406 	public void convertArrayToObjectAssignableTargetType() {
407 		Long[] array = new Long[] { 3L };
408 		Long[] result = (Long[]) conversionService.convert(array, Object.class);
409 		assertArrayEquals(array, result);
410 	}
411 
412 	@Test
413 	public void convertObjectToArray() {
414 		Object[] result = conversionService.convert(3L, Object[].class);
415 		assertEquals(1, result.length);
416 		assertEquals(3L, result[0]);
417 	}
418 
419 	@Test
420 	public void convertObjectToArrayWithElementConversion() {
421 		Integer[] result = conversionService.convert(3L, Integer[].class);
422 		assertEquals(1, result.length);
423 		assertEquals(new Integer(3), result[0]);
424 	}
425 
426 	@Test
427 	public void convertCollectionToArray() {
428 		List<String> list = new ArrayList<String>();
429 		list.add("1");
430 		list.add("2");
431 		list.add("3");
432 		String[] result = conversionService.convert(list, String[].class);
433 		assertEquals("1", result[0]);
434 		assertEquals("2", result[1]);
435 		assertEquals("3", result[2]);
436 	}
437 
438 	@Test
439 	public void convertCollectionToArrayWithElementConversion() {
440 		List<String> list = new ArrayList<String>();
441 		list.add("1");
442 		list.add("2");
443 		list.add("3");
444 		Integer[] result = conversionService.convert(list, Integer[].class);
445 		assertEquals(new Integer(1), result[0]);
446 		assertEquals(new Integer(2), result[1]);
447 		assertEquals(new Integer(3), result[2]);
448 	}
449 
450 	@Test
451 	public void convertCollectionToString() {
452 		List<String> list = Arrays.asList(new String[] { "foo", "bar" });
453 		String result = conversionService.convert(list, String.class);
454 		assertEquals("foo,bar", result);
455 	}
456 
457 	@Test
458 	public void convertCollectionToStringWithElementConversion() throws Exception {
459 		List<Integer> list = Arrays.asList(new Integer[] { 3, 5 });
460 		String result = (String) conversionService.convert(list,
461 				new TypeDescriptor(getClass().getField("genericList")), TypeDescriptor.valueOf(String.class));
462 		assertEquals("3,5", result);
463 	}
464 
465 	@Test
466 	public void convertStringToCollection() {
467 		List result = conversionService.convert("1,2,3", List.class);
468 		assertEquals(3, result.size());
469 		assertEquals("1", result.get(0));
470 		assertEquals("2", result.get(1));
471 		assertEquals("3", result.get(2));
472 	}
473 
474 	@Test
475 	public void convertStringToCollectionWithElementConversion() throws Exception {
476 		List result = (List) conversionService.convert("1,2,3", TypeDescriptor.valueOf(String.class),
477 				new TypeDescriptor(getClass().getField("genericList")));
478 		assertEquals(3, result.size());
479 		assertEquals(new Integer(1), result.get(0));
480 		assertEquals(new Integer(2), result.get(1));
481 		assertEquals(new Integer(3), result.get(2));
482 	}
483 
484 	@Test
485 	public void convertEmptyStringToCollection() {
486 		Collection result = conversionService.convert("", Collection.class);
487 		assertEquals(0, result.size());
488 	}
489 
490 	@Test
491 	public void convertCollectionToObject() {
492 		List<Long> list = Collections.singletonList(3L);
493 		Long result = conversionService.convert(list, Long.class);
494 		assertEquals(new Long(3), result);
495 	}
496 
497 	@Test
498 	public void convertCollectionToObjectWithElementConversion() {
499 		List<String> list = Collections.singletonList("3");
500 		Integer result = conversionService.convert(list, Integer.class);
501 		assertEquals(new Integer(3), result);
502 	}
503 
504 	@Test
505 	public void convertCollectionToObjectAssignableTarget() throws Exception {
506 		Collection<String> source = new ArrayList<String>();
507 		source.add("foo");
508 		Object result = conversionService.convert(source, new TypeDescriptor(getClass().getField("assignableTarget")));
509 		assertEquals(source, result);
510 	}
511 
512 	@Test
513 	public void convertCollectionToObjectWithCustomConverter() throws Exception {
514 		List<String> source = new ArrayList<String>();
515 		source.add("A");
516 		source.add("B");
517 		conversionService.addConverter(new Converter<List, ListWrapper>() {
518 			@Override
519 			public ListWrapper convert(List source) {
520 				return new ListWrapper(source);
521 			}
522 		});
523 		ListWrapper result = conversionService.convert(source, ListWrapper.class);
524 		assertSame(source, result.getList());
525 	}
526 
527 	@Test
528 	public void convertObjectToCollection() {
529 		List<String> result = (List<String>) conversionService.convert(3L, List.class);
530 		assertEquals(1, result.size());
531 		assertEquals(3L, result.get(0));
532 	}
533 
534 	@Test
535 	public void convertObjectToCollectionWithElementConversion() throws Exception {
536 		List<Integer> result = (List<Integer>) conversionService.convert(3L, TypeDescriptor.valueOf(Long.class),
537 				new TypeDescriptor(getClass().getField("genericList")));
538 		assertEquals(1, result.size());
539 		assertEquals(new Integer(3), result.get(0));
540 	}
541 
542 	@Test
543 	public void convertArrayToArray() {
544 		Integer[] result = conversionService.convert(new String[] { "1", "2", "3" }, Integer[].class);
545 		assertEquals(new Integer(1), result[0]);
546 		assertEquals(new Integer(2), result[1]);
547 		assertEquals(new Integer(3), result[2]);
548 	}
549 
550 	@Test
551 	public void convertArrayToPrimitiveArray() {
552 		int[] result = conversionService.convert(new String[] { "1", "2", "3" }, int[].class);
553 		assertEquals(1, result[0]);
554 		assertEquals(2, result[1]);
555 		assertEquals(3, result[2]);
556 	}
557 
558 	@Test
559 	public void convertArrayToArrayAssignable() {
560 		int[] result = conversionService.convert(new int[] { 1, 2, 3 }, int[].class);
561 		assertEquals(1, result[0]);
562 		assertEquals(2, result[1]);
563 		assertEquals(3, result[2]);
564 	}
565 
566 	@Test
567 	public void convertCollectionToCollection() throws Exception {
568 		Set<String> foo = new LinkedHashSet<String>();
569 		foo.add("1");
570 		foo.add("2");
571 		foo.add("3");
572 		List<Integer> bar = (List<Integer>) conversionService.convert(foo, TypeDescriptor.forObject(foo),
573 				new TypeDescriptor(getClass().getField("genericList")));
574 		assertEquals(new Integer(1), bar.get(0));
575 		assertEquals(new Integer(2), bar.get(1));
576 		assertEquals(new Integer(3), bar.get(2));
577 	}
578 
579 	@Test
580 	public void convertCollectionToCollectionNull() throws Exception {
581 		List<Integer> bar = (List<Integer>) conversionService.convert(null,
582 				TypeDescriptor.valueOf(LinkedHashSet.class), new TypeDescriptor(getClass().getField("genericList")));
583 		assertNull(bar);
584 	}
585 
586 	@Test
587 	public void convertCollectionToCollectionNotGeneric() throws Exception {
588 		Set<String> foo = new LinkedHashSet<String>();
589 		foo.add("1");
590 		foo.add("2");
591 		foo.add("3");
592 		List bar = (List) conversionService.convert(foo, TypeDescriptor.valueOf(LinkedHashSet.class), TypeDescriptor
593 				.valueOf(List.class));
594 		assertEquals("1", bar.get(0));
595 		assertEquals("2", bar.get(1));
596 		assertEquals("3", bar.get(2));
597 	}
598 
599 	@Test
600 	public void convertCollectionToCollectionSpecialCaseSourceImpl() throws Exception {
601 		Map map = new LinkedHashMap();
602 		map.put("1", "1");
603 		map.put("2", "2");
604 		map.put("3", "3");
605 		Collection values = map.values();
606 		List<Integer> bar = (List<Integer>) conversionService.convert(values,
607 				TypeDescriptor.forObject(values), new TypeDescriptor(getClass().getField("genericList")));
608 		assertEquals(3, bar.size());
609 		assertEquals(new Integer(1), bar.get(0));
610 		assertEquals(new Integer(2), bar.get(1));
611 		assertEquals(new Integer(3), bar.get(2));
612 	}
613 
614 	@Test
615 	public void collection() {
616 		List<String> strings = new ArrayList<String>();
617 		strings.add("3");
618 		strings.add("9");
619 		List<Integer> integers = (List<Integer>) conversionService.convert(strings, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)));
620 		assertEquals(new Integer(3), integers.get(0));
621 		assertEquals(new Integer(9), integers.get(1));
622 	}
623 
624 	public Map<Integer, FooEnum> genericMap = new HashMap<Integer, FooEnum>();
625 
626 	@Test
627 	public void convertMapToMap() throws Exception {
628 		Map<String, String> foo = new HashMap<String, String>();
629 		foo.put("1", "BAR");
630 		foo.put("2", "BAZ");
631 		Map<String, FooEnum> map = (Map<String, FooEnum>) conversionService.convert(foo,
632 				TypeDescriptor.forObject(foo), new TypeDescriptor(getClass().getField("genericMap")));
633 		assertEquals(FooEnum.BAR, map.get(1));
634 		assertEquals(FooEnum.BAZ, map.get(2));
635 	}
636 
637 	@Test
638 	public void map() {
639 		Map<String, String> strings = new HashMap<String, String>();
640 		strings.put("3", "9");
641 		strings.put("6", "31");
642 		Map<Integer, Integer> integers = (Map<Integer, Integer>) conversionService.convert(strings, TypeDescriptor.map(Map.class, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(Integer.class)));
643 		assertEquals(new Integer(9), integers.get(3));
644 		assertEquals(new Integer(31), integers.get(6));
645 	}
646 
647 	@Test
648 	public void convertPropertiesToString() {
649 		Properties foo = new Properties();
650 		foo.setProperty("1", "BAR");
651 		foo.setProperty("2", "BAZ");
652 		String result = conversionService.convert(foo, String.class);
653 		assertTrue(result.contains("1=BAR"));
654 		assertTrue(result.contains("2=BAZ"));
655 	}
656 
657 	@Test
658 	public void convertStringToProperties() {
659 		Properties result = conversionService.convert("a=b\nc=2\nd=", Properties.class);
660 		assertEquals(3, result.size());
661 		assertEquals("b", result.getProperty("a"));
662 		assertEquals("2", result.getProperty("c"));
663 		assertEquals("", result.getProperty("d"));
664 	}
665 
666 	@Test
667 	public void convertStringToPropertiesWithSpaces() {
668 		Properties result = conversionService.convert("   foo=bar\n   bar=baz\n    baz=boop", Properties.class);
669 		assertEquals("bar", result.get("foo"));
670 		assertEquals("baz", result.get("bar"));
671 		assertEquals("boop", result.get("baz"));
672 	}
673 
674 	// generic object conversion
675 
676 	@Test
677 	public void convertObjectToStringValueOfMethodPresent() {
678 		assertEquals("123456789", conversionService.convert(ISBN.valueOf("123456789"), String.class));
679 	}
680 
681 	@Test
682 	public void convertObjectToStringStringConstructorPresent() {
683 		assertEquals("123456789", conversionService.convert(new SSN("123456789"), String.class));
684 	}
685 
686 	@Test
687 	public void convertObjectToStringWithJavaTimeOfMethodPresent() {
688 		assertTrue(conversionService.convert(ZoneId.of("GMT+1"), String.class).startsWith("GMT+"));
689 	}
690 
691 	@Test
692 	public void convertObjectToStringNotSupported() {
693 		assertFalse(conversionService.canConvert(TestEntity.class, String.class));
694 	}
695 
696 	@Test
697 	public void convertObjectToObjectValueOfMethod() {
698 		assertEquals(ISBN.valueOf("123456789"), conversionService.convert("123456789", ISBN.class));
699 	}
700 
701 	@Test
702 	public void convertObjectToObjectConstructor() {
703 		assertEquals(new SSN("123456789"), conversionService.convert("123456789", SSN.class));
704 		assertEquals("123456789", conversionService.convert(new SSN("123456789"), String.class));
705 	}
706 
707 	@Test
708 	public void convertObjectToObjectWithJavaTimeOfMethod() {
709 		assertEquals(ZoneId.of("GMT+1"), conversionService.convert("GMT+1", ZoneId.class));
710 	}
711 
712 	@Test(expected=ConverterNotFoundException.class)
713 	public void convertObjectToObjectNoValueOFMethodOrConstructor() {
714 		conversionService.convert(new Long(3), SSN.class);
715 	}
716 
717 	@Test
718 	public void convertObjectToObjectFinderMethod() {
719 		TestEntity e = conversionService.convert(1L, TestEntity.class);
720 		assertEquals(new Long(1), e.getId());
721 	}
722 
723 	@Test
724 	public void convertObjectToObjectFinderMethodWithNull() {
725 		TestEntity e = (TestEntity) conversionService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(TestEntity.class));
726 		assertNull(e);
727 	}
728 
729 	@Test
730 	public void convertObjectToObjectFinderMethodWithIdConversion() {
731 		TestEntity e = conversionService.convert("1", TestEntity.class);
732 		assertEquals(new Long(1), e.getId());
733 	}
734 
735 	@Test
736 	public void convertCharArrayToString() throws Exception {
737 		String converted = conversionService.convert(new char[] { 'a', 'b', 'c' }, String.class);
738 		assertThat(converted, equalTo("a,b,c"));
739 	}
740 
741 	@Test
742 	public void convertStringToCharArray() throws Exception {
743 		char[] converted = conversionService.convert("a,b,c", char[].class);
744 		assertThat(converted, equalTo(new char[] { 'a', 'b', 'c' }));
745 	}
746 
747 	@Test
748 	public void convertStringToCustomCharArray() throws Exception {
749 		conversionService.addConverter(new Converter<String, char[]>() {
750 			@Override
751 			public char[] convert(String source) {
752 				return source.toCharArray();
753 			}
754 		});
755 		char[] converted = conversionService.convert("abc", char[].class);
756 		assertThat(converted, equalTo(new char[] { 'a', 'b', 'c' }));
757 	}
758 
759 	@Test
760 	public void multidimensionalArrayToListConversionShouldConvertEntriesCorrectly() {
761 		String[][] grid = new String[][] { new String[] { "1", "2", "3", "4" }, new String[] { "5", "6", "7", "8" },
762 				new String[] { "9", "10", "11", "12" } };
763 		List<String[]> converted = conversionService.convert(grid, List.class);
764 		String[][] convertedBack = conversionService.convert(converted, String[][].class);
765 		assertArrayEquals(grid, convertedBack);
766 	}
767 
768 	@Test
769 	@SuppressWarnings("unchecked")
770 	public void convertObjectToOptional() {
771 		Method method = ClassUtils.getMethod(TestEntity.class, "handleOptionalValue", Optional.class);
772 		MethodParameter parameter = new MethodParameter(method, 0);
773 		TypeDescriptor descriptor = new TypeDescriptor(parameter);
774 		Object actual = conversionService.convert("1,2,3", TypeDescriptor.valueOf(String.class), descriptor);
775 		assertEquals(Optional.class, actual.getClass());
776 		assertEquals(Arrays.asList(1, 2, 3), ((Optional<List<Integer>>) actual).get());
777 	}
778 
779 	@Test
780 	public void convertObjectToOptionalNull() {
781 		assertSame(Optional.empty(), conversionService.convert(null, TypeDescriptor.valueOf(Object.class), TypeDescriptor.valueOf(Optional.class)));
782 		assertSame(Optional.empty(), conversionService.convert(null, Optional.class));
783 	}
784 
785 	@Test
786 	public void convertExistingOptional() {
787 		assertSame(Optional.empty(), conversionService.convert(Optional.empty(), TypeDescriptor.valueOf(Object.class),
788 				TypeDescriptor.valueOf(Optional.class)));
789 		assertSame(Optional.empty(), conversionService.convert(Optional.empty(), Optional.class));
790 	}
791 
792 
793 	@SuppressWarnings("serial")
794 	public static class CustomNumber extends Number {
795 
796 		@Override
797 		public double doubleValue() {
798 			return 0;
799 		}
800 
801 		@Override
802 		public float floatValue() {
803 			return 0;
804 		}
805 
806 		@Override
807 		public int intValue() {
808 			return 0;
809 		}
810 
811 		@Override
812 		public long longValue() {
813 			return 0;
814 		}
815 	}
816 
817 
818 	public static class TestEntity {
819 
820 		private Long id;
821 
822 		public TestEntity(Long id) {
823 			this.id = id;
824 		}
825 
826 		public Long getId() {
827 			return id;
828 		}
829 
830 		public static TestEntity findTestEntity(Long id) {
831 			return new TestEntity(id);
832 		}
833 
834 		public void handleOptionalValue(Optional<List<Integer>> value) {
835 		}
836 	}
837 
838 
839 	private static class ListWrapper {
840 
841 		private List<?> list;
842 
843 		public ListWrapper(List<?> list) {
844 			this.list = list;
845 		}
846 
847 		public List<?> getList() {
848 			return list;
849 		}
850 	}
851 
852 
853 	public Object assignableTarget;
854 
855 
856 	private static class SSN {
857 
858 		private String value;
859 
860 		public SSN(String value) {
861 			this.value = value;
862 		}
863 
864 		@Override
865 		public boolean equals(Object o) {
866 			if (!(o instanceof SSN)) {
867 				return false;
868 			}
869 			SSN ssn = (SSN) o;
870 			return this.value.equals(ssn.value);
871 		}
872 
873 		@Override
874 		public int hashCode() {
875 			return value.hashCode();
876 		}
877 
878 		@Override
879 		public String toString() {
880 			return value;
881 		}
882 	}
883 
884 
885 	private static class ISBN {
886 
887 		private String value;
888 
889 		private ISBN(String value) {
890 			this.value = value;
891 		}
892 
893 		@Override
894 		public boolean equals(Object o) {
895 			if (!(o instanceof ISBN)) {
896 				return false;
897 			}
898 			ISBN isbn = (ISBN) o;
899 			return this.value.equals(isbn.value);
900 		}
901 
902 		@Override
903 		public int hashCode() {
904 			return value.hashCode();
905 		}
906 
907 		@Override
908 		public String toString() {
909 			return value;
910 		}
911 
912 		public static ISBN valueOf(String value) {
913 			return new ISBN(value);
914 		}
915 	}
916 
917 }