1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.core.convert.support;
18
19 import java.awt.Color;
20 import java.awt.SystemColor;
21 import java.lang.annotation.Retention;
22 import java.lang.annotation.RetentionPolicy;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.EnumSet;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.LinkedHashMap;
32 import java.util.LinkedList;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.UUID;
37
38 import org.junit.Test;
39
40 import org.springframework.core.convert.ConversionFailedException;
41 import org.springframework.core.convert.ConverterNotFoundException;
42 import org.springframework.core.convert.TypeDescriptor;
43 import org.springframework.core.convert.converter.ConditionalConverter;
44 import org.springframework.core.convert.converter.Converter;
45 import org.springframework.core.convert.converter.ConverterFactory;
46 import org.springframework.core.convert.converter.GenericConverter;
47 import org.springframework.core.io.DescriptiveResource;
48 import org.springframework.core.io.Resource;
49 import org.springframework.tests.Assume;
50 import org.springframework.tests.TestGroup;
51 import org.springframework.util.StopWatch;
52 import org.springframework.util.StringUtils;
53
54 import static org.hamcrest.Matchers.*;
55 import static org.junit.Assert.*;
56
57
58
59
60
61
62
63 public class GenericConversionServiceTests {
64
65 private GenericConversionService conversionService = new GenericConversionService();
66
67
68 @Test
69 public void canConvert() {
70 assertFalse(conversionService.canConvert(String.class, Integer.class));
71 conversionService.addConverterFactory(new StringToNumberConverterFactory());
72 assertTrue(conversionService.canConvert(String.class, Integer.class));
73 }
74
75 @Test
76 public void canConvertAssignable() {
77 assertTrue(conversionService.canConvert(String.class, String.class));
78 assertTrue(conversionService.canConvert(Integer.class, Number.class));
79 assertTrue(conversionService.canConvert(boolean.class, boolean.class));
80 assertTrue(conversionService.canConvert(boolean.class, Boolean.class));
81 }
82
83 @Test
84 public void canConvertIllegalArgumentNullTargetType() {
85 try {
86 assertFalse(conversionService.canConvert(String.class, null));
87 fail("Should have failed");
88 }
89 catch (IllegalArgumentException ex) {
90 }
91 try {
92 assertFalse(conversionService.canConvert(TypeDescriptor.valueOf(String.class), null));
93 fail("Should have failed");
94 }
95 catch (IllegalArgumentException ex) {
96 }
97 }
98
99 @Test
100 public void canConvertNullSourceType() {
101 assertTrue(conversionService.canConvert(null, Integer.class));
102 assertTrue(conversionService.canConvert(null, TypeDescriptor.valueOf(Integer.class)));
103 }
104
105 @Test
106 public void convert() {
107 conversionService.addConverterFactory(new StringToNumberConverterFactory());
108 assertEquals(new Integer(3), conversionService.convert("3", Integer.class));
109 }
110
111 @Test
112 public void convertNullSource() {
113 assertEquals(null, conversionService.convert(null, Integer.class));
114 }
115
116 @Test(expected = ConversionFailedException.class)
117 public void convertNullSourcePrimitiveTarget() {
118 assertEquals(null, conversionService.convert(null, int.class));
119 }
120
121 @Test(expected = ConversionFailedException.class)
122 public void convertNullSourcePrimitiveTargetTypeDescriptor() {
123 conversionService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(int.class));
124 }
125
126 @Test(expected = IllegalArgumentException.class)
127 public void convertNotNullSourceNullSourceTypeDescriptor() {
128 conversionService.convert("3", null, TypeDescriptor.valueOf(int.class));
129 }
130
131 @Test
132 public void convertAssignableSource() {
133 assertEquals(Boolean.FALSE, conversionService.convert(false, boolean.class));
134 assertEquals(Boolean.FALSE, conversionService.convert(false, Boolean.class));
135 }
136
137 @Test
138 public void converterNotFound() {
139 try {
140 conversionService.convert("3", Integer.class);
141 fail("Should have thrown an exception");
142 }
143 catch (ConverterNotFoundException e) {
144 }
145 }
146
147 @Test
148 @SuppressWarnings("rawtypes")
149 public void addConverterNoSourceTargetClassInfoAvailable() {
150 try {
151 conversionService.addConverter(new Converter() {
152 @Override
153 public Object convert(Object source) {
154 return source;
155 }
156 });
157 fail("Should have failed");
158 }
159 catch (IllegalArgumentException ex) {
160 }
161 }
162
163 @Test
164 public void sourceTypeIsVoid() {
165 GenericConversionService conversionService = new GenericConversionService();
166 assertFalse(conversionService.canConvert(void.class, String.class));
167 }
168
169 @Test
170 public void targetTypeIsVoid() {
171 GenericConversionService conversionService = new GenericConversionService();
172 assertFalse(conversionService.canConvert(String.class, void.class));
173 }
174
175 @Test
176 public void convertNull() {
177 assertNull(conversionService.convert(null, Integer.class));
178 }
179
180 @Test(expected = IllegalArgumentException.class)
181 public void convertNullTargetClass() {
182 assertNull(conversionService.convert("3", (Class<?>) null));
183 assertNull(conversionService.convert("3", TypeDescriptor.valueOf(String.class), null));
184 }
185
186 @Test(expected = IllegalArgumentException.class)
187 public void convertNullTypeDescriptor() {
188 assertNull(conversionService.convert("3", TypeDescriptor.valueOf(String.class), null));
189 }
190
191 @Test(expected = IllegalArgumentException.class)
192 public void convertWrongSourceTypeDescriptor() {
193 conversionService.convert("3", TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(Long.class));
194 }
195
196 @Test
197 public void convertWrongTypeArgument() {
198 conversionService.addConverterFactory(new StringToNumberConverterFactory());
199 try {
200 conversionService.convert("BOGUS", Integer.class);
201 fail("Should have failed");
202 }
203 catch (ConversionFailedException e) {
204
205 }
206 }
207
208 @Test
209 public void convertSuperSourceType() {
210 conversionService.addConverter(new Converter<CharSequence, Integer>() {
211 @Override
212 public Integer convert(CharSequence source) {
213 return Integer.valueOf(source.toString());
214 }
215 });
216 Integer result = conversionService.convert("3", Integer.class);
217 assertEquals(new Integer(3), result);
218 }
219
220
221 @Test(expected = ConverterNotFoundException.class)
222 public void convertSuperTarget() {
223 conversionService.addConverter(new ColorConverter());
224 conversionService.convert("#000000", SystemColor.class);
225 }
226
227 public class ColorConverter implements Converter<String, Color> {
228 @Override
229 public Color convert(String source) { if (!source.startsWith("#")) source = "#" + source; return Color.decode(source); }
230 }
231
232 @Test
233 public void convertObjectToPrimitive() {
234 assertFalse(conversionService.canConvert(String.class, boolean.class));
235 conversionService.addConverter(new StringToBooleanConverter());
236 assertTrue(conversionService.canConvert(String.class, boolean.class));
237 Boolean b = conversionService.convert("true", boolean.class);
238 assertEquals(Boolean.TRUE, b);
239 assertTrue(conversionService.canConvert(TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(boolean.class)));
240 b = (Boolean) conversionService.convert("true", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(boolean.class));
241 assertEquals(Boolean.TRUE, b);
242 }
243
244 @Test
245 public void convertObjectToPrimitiveViaConverterFactory() {
246 assertFalse(conversionService.canConvert(String.class, int.class));
247 conversionService.addConverterFactory(new StringToNumberConverterFactory());
248 assertTrue(conversionService.canConvert(String.class, int.class));
249 Integer three = conversionService.convert("3", int.class);
250 assertEquals(3, three.intValue());
251 }
252
253 @Test
254 public void genericConverterDelegatingBackToConversionServiceConverterNotFound() {
255 conversionService.addConverter(new ObjectToArrayConverter(conversionService));
256 assertFalse(conversionService.canConvert(String.class, Integer[].class));
257 try {
258 conversionService.convert("3,4,5", Integer[].class);
259 fail("should have failed");
260 }
261 catch (ConverterNotFoundException ex) {
262 }
263 }
264
265 @Test
266 public void testListToIterableConversion() {
267 GenericConversionService conversionService = new GenericConversionService();
268 List<Object> raw = new ArrayList<Object>();
269 raw.add("one");
270 raw.add("two");
271 Object converted = conversionService.convert(raw, Iterable.class);
272 assertSame(raw, converted);
273 }
274
275 @Test
276 public void testListToObjectConversion() {
277 GenericConversionService conversionService = new GenericConversionService();
278 List<Object> raw = new ArrayList<Object>();
279 raw.add("one");
280 raw.add("two");
281 Object converted = conversionService.convert(raw, Object.class);
282 assertSame(raw, converted);
283 }
284
285 @Test
286 public void testMapToObjectConversion() {
287 GenericConversionService conversionService = new GenericConversionService();
288 Map<Object, Object> raw = new HashMap<Object, Object>();
289 raw.put("key", "value");
290 Object converted = conversionService.convert(raw, Object.class);
291 assertSame(raw, converted);
292 }
293
294 @Test
295 public void testInterfaceToString() {
296 GenericConversionService conversionService = new GenericConversionService();
297 conversionService.addConverter(new MyBaseInterfaceToStringConverter());
298 conversionService.addConverter(new ObjectToStringConverter());
299 Object converted = conversionService.convert(new MyInterfaceImplementer(), String.class);
300 assertEquals("RESULT", converted);
301 }
302
303 @Test
304 public void testInterfaceArrayToStringArray() {
305 GenericConversionService conversionService = new GenericConversionService();
306 conversionService.addConverter(new MyBaseInterfaceToStringConverter());
307 conversionService.addConverter(new ArrayToArrayConverter(conversionService));
308 String[] converted = conversionService.convert(new MyInterface[] {new MyInterfaceImplementer()}, String[].class);
309 assertEquals("RESULT", converted[0]);
310 }
311
312 @Test
313 public void testObjectArrayToStringArray() {
314 GenericConversionService conversionService = new GenericConversionService();
315 conversionService.addConverter(new MyBaseInterfaceToStringConverter());
316 conversionService.addConverter(new ArrayToArrayConverter(conversionService));
317 String[] converted = conversionService.convert(new MyInterfaceImplementer[] {new MyInterfaceImplementer()}, String[].class);
318 assertEquals("RESULT", converted[0]);
319 }
320
321 @Test
322 public void testStringArrayToResourceArray() {
323 GenericConversionService conversionService = new DefaultConversionService();
324 conversionService.addConverter(new MyStringArrayToResourceArrayConverter());
325 Resource[] converted = conversionService.convert(new String[] {"x1", "z3"}, Resource[].class);
326 assertEquals(2, converted.length);
327 assertEquals("1", converted[0].getDescription());
328 assertEquals("3", converted[1].getDescription());
329 }
330
331 @Test
332 public void testStringArrayToIntegerArray() {
333 GenericConversionService conversionService = new DefaultConversionService();
334 conversionService.addConverter(new MyStringArrayToIntegerArrayConverter());
335 Integer[] converted = conversionService.convert(new String[] {"x1", "z3"}, Integer[].class);
336 assertEquals(2, converted.length);
337 assertEquals(1, converted[0].intValue());
338 assertEquals(3, converted[1].intValue());
339 }
340
341 @Test
342 public void testStringToIntegerArray() {
343 GenericConversionService conversionService = new DefaultConversionService();
344 conversionService.addConverter(new MyStringToIntegerArrayConverter());
345 Integer[] converted = conversionService.convert("x1,z3", Integer[].class);
346 assertEquals(2, converted.length);
347 assertEquals(1, converted[0].intValue());
348 assertEquals(3, converted[1].intValue());
349 }
350
351 @Test
352 public void testWildcardMap() throws Exception {
353 GenericConversionService conversionService = new DefaultConversionService();
354 Map<String, String> input = new LinkedHashMap<String, String>();
355 input.put("key", "value");
356 Object converted = conversionService.convert(input, TypeDescriptor.forObject(input), new TypeDescriptor(getClass().getField("wildcardMap")));
357 assertEquals(input, converted);
358 }
359
360 @Test
361 public void testListOfList() {
362 GenericConversionService service = new DefaultConversionService();
363 List<String> list1 = Arrays.asList("Foo", "Bar");
364 List<String> list2 = Arrays.asList("Baz", "Boop");
365 List<List<String>> list = Arrays.asList(list1, list2);
366 String result = service.convert(list, String.class);
367 assertNotNull(result);
368 assertEquals("Foo,Bar,Baz,Boop", result);
369 }
370
371 @Test
372 public void testStringToString() {
373 GenericConversionService service = new DefaultConversionService();
374 String value = "myValue";
375 String result = service.convert(value, String.class);
376 assertSame(value, result);
377 }
378
379 @Test
380 public void testStringToObject() {
381 GenericConversionService service = new DefaultConversionService();
382 String value = "myValue";
383 Object result = service.convert(value, Object.class);
384 assertSame(value, result);
385 }
386
387 @Test
388 public void testIgnoreCopyConstructor() {
389 GenericConversionService service = new DefaultConversionService();
390 WithCopyConstructor value = new WithCopyConstructor();
391 Object result = service.convert(value, WithCopyConstructor.class);
392 assertSame(value, result);
393 }
394
395 @Test
396 public void testConvertUUID() {
397 GenericConversionService service = new DefaultConversionService();
398 UUID uuid = UUID.randomUUID();
399 String convertToString = service.convert(uuid, String.class);
400 UUID convertToUUID = service.convert(convertToString, UUID.class);
401 assertEquals(uuid, convertToUUID);
402 }
403
404 @Test
405 public void testPerformance1() {
406 Assume.group(TestGroup.PERFORMANCE);
407 GenericConversionService conversionService = new DefaultConversionService();
408 StopWatch watch = new StopWatch("integer->string conversionPerformance");
409 watch.start("convert 4,000,000 with conversion service");
410 for (int i = 0; i < 4000000; i++) {
411 conversionService.convert(3, String.class);
412 }
413 watch.stop();
414 watch.start("convert 4,000,000 manually");
415 for (int i = 0; i < 4000000; i++) {
416 new Integer(3).toString();
417 }
418 watch.stop();
419 System.out.println(watch.prettyPrint());
420 }
421
422 @Test
423 public void testPerformance2() throws Exception {
424 Assume.group(TestGroup.PERFORMANCE);
425 GenericConversionService conversionService = new DefaultConversionService();
426 StopWatch watch = new StopWatch("list<string> -> list<integer> conversionPerformance");
427 watch.start("convert 4,000,000 with conversion service");
428 List<String> source = new LinkedList<String>();
429 source.add("1");
430 source.add("2");
431 source.add("3");
432 TypeDescriptor td = new TypeDescriptor(getClass().getField("list"));
433 for (int i = 0; i < 1000000; i++) {
434 conversionService.convert(source, TypeDescriptor.forObject(source), td);
435 }
436 watch.stop();
437 watch.start("convert 4,000,000 manually");
438 for (int i = 0; i < 4000000; i++) {
439 List<Integer> target = new ArrayList<Integer>(source.size());
440 for (String element : source) {
441 target.add(Integer.valueOf(element));
442 }
443 }
444 watch.stop();
445 System.out.println(watch.prettyPrint());
446 }
447
448 @Test
449 public void testPerformance3() throws Exception {
450 Assume.group(TestGroup.PERFORMANCE);
451 GenericConversionService conversionService = new DefaultConversionService();
452 StopWatch watch = new StopWatch("map<string, string> -> map<string, integer> conversionPerformance");
453 watch.start("convert 4,000,000 with conversion service");
454 Map<String, String> source = new HashMap<String, String>();
455 source.put("1", "1");
456 source.put("2", "2");
457 source.put("3", "3");
458 TypeDescriptor td = new TypeDescriptor(getClass().getField("map"));
459 for (int i = 0; i < 1000000; i++) {
460 conversionService.convert(source, TypeDescriptor.forObject(source), td);
461 }
462 watch.stop();
463 watch.start("convert 4,000,000 manually");
464 for (int i = 0; i < 4000000; i++) {
465 Map<String, Integer> target = new HashMap<String, Integer>(source.size());
466 for (Map.Entry<String, String> entry : source.entrySet()) {
467 target.put(entry.getKey(), Integer.valueOf(entry.getValue()));
468 }
469 }
470 watch.stop();
471 System.out.println(watch.prettyPrint());
472 }
473
474 @Test
475 public void emptyListToArray() {
476 conversionService.addConverter(new CollectionToArrayConverter(conversionService));
477 conversionService.addConverterFactory(new StringToNumberConverterFactory());
478 List<String> list = new ArrayList<String>();
479 TypeDescriptor sourceType = TypeDescriptor.forObject(list);
480 TypeDescriptor targetType = TypeDescriptor.valueOf(String[].class);
481 assertTrue(conversionService.canConvert(sourceType, targetType));
482 assertEquals(0, ((String[]) conversionService.convert(list, sourceType, targetType)).length);
483 }
484
485 @Test
486 public void emptyListToObject() {
487 conversionService.addConverter(new CollectionToObjectConverter(conversionService));
488 conversionService.addConverterFactory(new StringToNumberConverterFactory());
489 List<String> list = new ArrayList<String>();
490 TypeDescriptor sourceType = TypeDescriptor.forObject(list);
491 TypeDescriptor targetType = TypeDescriptor.valueOf(Integer.class);
492 assertTrue(conversionService.canConvert(sourceType, targetType));
493 assertNull(conversionService.convert(list, sourceType, targetType));
494 }
495
496 @Test
497 public void stringToArrayCanConvert() {
498 conversionService.addConverter(new StringToArrayConverter(conversionService));
499 assertFalse(conversionService.canConvert(String.class, Integer[].class));
500 conversionService.addConverterFactory(new StringToNumberConverterFactory());
501 assertTrue(conversionService.canConvert(String.class, Integer[].class));
502 }
503
504 @Test
505 public void stringToCollectionCanConvert() throws Exception {
506 conversionService.addConverter(new StringToCollectionConverter(conversionService));
507 assertTrue(conversionService.canConvert(String.class, Collection.class));
508 TypeDescriptor targetType = new TypeDescriptor(getClass().getField("integerCollection"));
509 assertFalse(conversionService.canConvert(TypeDescriptor.valueOf(String.class), targetType));
510 conversionService.addConverterFactory(new StringToNumberConverterFactory());
511 assertTrue(conversionService.canConvert(TypeDescriptor.valueOf(String.class), targetType));
512 }
513
514 @Test
515 public void testConvertiblePairsInSet() {
516 Set<GenericConverter.ConvertiblePair> set = new HashSet<GenericConverter.ConvertiblePair>();
517 set.add(new GenericConverter.ConvertiblePair(Number.class, String.class));
518 assert set.contains(new GenericConverter.ConvertiblePair(Number.class, String.class));
519 }
520
521 @Test
522 public void testConvertiblePairEqualsAndHash() {
523 GenericConverter.ConvertiblePair pair = new GenericConverter.ConvertiblePair(Number.class, String.class);
524 GenericConverter.ConvertiblePair pairEqual = new GenericConverter.ConvertiblePair(Number.class, String.class);
525 assertEquals(pair, pairEqual);
526 assertEquals(pair.hashCode(), pairEqual.hashCode());
527 }
528
529 @Test
530 public void testConvertiblePairDifferentEqualsAndHash() {
531 GenericConverter.ConvertiblePair pair = new GenericConverter.ConvertiblePair(Number.class, String.class);
532 GenericConverter.ConvertiblePair pairOpposite = new GenericConverter.ConvertiblePair(String.class, Number.class);
533 assertFalse(pair.equals(pairOpposite));
534 assertFalse(pair.hashCode() == pairOpposite.hashCode());
535 }
536
537 @Test
538 public void convertPrimitiveArray() {
539 GenericConversionService conversionService = new DefaultConversionService();
540 byte[] byteArray = new byte[] { 1, 2, 3 };
541 Byte[] converted = conversionService.convert(byteArray, Byte[].class);
542 assertTrue(Arrays.equals(converted, new Byte[] {1, 2, 3}));
543 }
544
545 @Test
546 public void canConvertIllegalArgumentNullTargetTypeFromClass() {
547 try {
548 conversionService.canConvert(String.class, null);
549 fail("Did not thow IllegalArgumentException");
550 }
551 catch (IllegalArgumentException ex) {
552 }
553 }
554
555 @Test
556 public void canConvertIllegalArgumentNullTargetTypeFromTypeDescriptor() {
557 try {
558 conversionService.canConvert(TypeDescriptor.valueOf(String.class), null);
559 fail("Did not thow IllegalArgumentException");
560 }
561 catch(IllegalArgumentException ex) {
562 }
563 }
564
565 @Test
566 @SuppressWarnings({ "rawtypes" })
567 public void convertHashMapValuesToList() {
568 GenericConversionService conversionService = new DefaultConversionService();
569 Map<String, Integer> hashMap = new LinkedHashMap<String, Integer>();
570 hashMap.put("1", 1);
571 hashMap.put("2", 2);
572 List converted = conversionService.convert(hashMap.values(), List.class);
573 assertEquals(Arrays.asList(1, 2), converted);
574 }
575
576 @Test
577 public void removeConvertible() {
578 conversionService.addConverter(new ColorConverter());
579 assertTrue(conversionService.canConvert(String.class, Color.class));
580 conversionService.removeConvertible(String.class, Color.class);
581 assertFalse(conversionService.canConvert(String.class, Color.class));
582 }
583
584 @Test
585 public void conditionalConverter() {
586 GenericConversionService conversionService = new GenericConversionService();
587 MyConditionalConverter converter = new MyConditionalConverter();
588 conversionService.addConverter(new ColorConverter());
589 conversionService.addConverter(converter);
590 assertEquals(Color.BLACK, conversionService.convert("#000000", Color.class));
591 assertTrue(converter.getMatchAttempts() > 0);
592 }
593
594 @Test
595 public void conditionalConverterFactory() {
596 GenericConversionService conversionService = new GenericConversionService();
597 MyConditionalConverterFactory converter = new MyConditionalConverterFactory();
598 conversionService.addConverter(new ColorConverter());
599 conversionService.addConverterFactory(converter);
600 assertEquals(Color.BLACK, conversionService.convert("#000000", Color.class));
601 assertTrue(converter.getMatchAttempts() > 0);
602 assertTrue(converter.getNestedMatchAttempts() > 0);
603 }
604
605 @Test
606 public void shouldNotSupportNullConvertibleTypesFromNonConditionalGenericConverter() {
607 GenericConversionService conversionService = new GenericConversionService();
608 GenericConverter converter = new GenericConverter() {
609 @Override
610 public Set<ConvertiblePair> getConvertibleTypes() {
611 return null;
612 }
613 @Override
614 public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
615 return null;
616 }
617 };
618 try {
619 conversionService.addConverter(converter);
620 fail("Did not throw");
621 }
622 catch (IllegalStateException ex) {
623 assertEquals("Only conditional converters may return null convertible types", ex.getMessage());
624 }
625 }
626
627 @Test
628 public void conditionalConversionForAllTypes() {
629 GenericConversionService conversionService = new GenericConversionService();
630 MyConditionalGenericConverter converter = new MyConditionalGenericConverter();
631 conversionService.addConverter(converter);
632 assertEquals((Integer) 3, conversionService.convert(3, Integer.class));
633 assertThat(converter.getSourceTypes().size(), greaterThan(2));
634 Iterator<TypeDescriptor> iterator = converter.getSourceTypes().iterator();
635 while(iterator.hasNext()) {
636 assertEquals(Integer.class, iterator.next().getType());
637 }
638 }
639
640 @Test
641 public void convertOptimizeArray() {
642
643 GenericConversionService conversionService = new DefaultConversionService();
644 byte[] byteArray = new byte[] { 1, 2, 3 };
645 byte[] converted = conversionService.convert(byteArray, byte[].class);
646 assertSame(byteArray, converted);
647 }
648
649 @Test
650 public void convertCannotOptimizeArray() {
651 GenericConversionService conversionService = new GenericConversionService();
652 conversionService.addConverter(new Converter<Byte, Byte>() {
653 @Override
654 public Byte convert(Byte source) {
655 return (byte) (source + 1);
656 }
657 });
658 DefaultConversionService.addDefaultConverters(conversionService);
659 byte[] byteArray = new byte[] { 1, 2, 3 };
660 byte[] converted = conversionService.convert(byteArray, byte[].class);
661 assertNotSame(byteArray, converted);
662 assertTrue(Arrays.equals(new byte[] {2, 3, 4}, converted));
663 }
664
665 @Test
666 public void testEnumToStringConversion() {
667 conversionService.addConverter(new EnumToStringConverter(conversionService));
668 String result = conversionService.convert(MyEnum.A, String.class);
669 assertEquals("A", result);
670 }
671
672 @Test
673 public void testSubclassOfEnumToString() throws Exception {
674 conversionService.addConverter(new EnumToStringConverter(conversionService));
675 String result = conversionService.convert(EnumWithSubclass.FIRST, String.class);
676 assertEquals("FIRST", result);
677 }
678
679 @Test
680 public void testEnumWithInterfaceToStringConversion() {
681
682 conversionService.addConverter(new EnumToStringConverter(conversionService));
683 conversionService.addConverter(new MyEnumInterfaceToStringConverter<MyEnum>());
684 String result = conversionService.convert(MyEnum.A, String.class);
685 assertEquals("1", result);
686 }
687
688 @Test
689 public void testStringToEnumWithInterfaceConversion() {
690 conversionService.addConverterFactory(new StringToEnumConverterFactory());
691 conversionService.addConverterFactory(new StringToMyEnumInterfaceConverterFactory());
692 assertEquals(MyEnum.A, conversionService.convert("1", MyEnum.class));
693 }
694
695 @Test
696 public void testStringToEnumWithBaseInterfaceConversion() {
697 conversionService.addConverterFactory(new StringToEnumConverterFactory());
698 conversionService.addConverterFactory(new StringToMyEnumBaseInterfaceConverterFactory());
699 assertEquals(MyEnum.A, conversionService.convert("base1", MyEnum.class));
700 }
701
702 @Test
703 public void testStringToEnumSet() throws Exception {
704 DefaultConversionService.addDefaultConverters(conversionService);
705 assertEquals(EnumSet.of(MyEnum.A),
706 conversionService.convert("A", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("enumSet"))));
707 }
708
709 @Test
710 public void convertNullAnnotatedStringToString() throws Exception {
711 DefaultConversionService.addDefaultConverters(conversionService);
712 String source = null;
713 TypeDescriptor sourceType = new TypeDescriptor(getClass().getField("annotatedString"));
714 TypeDescriptor targetType = TypeDescriptor.valueOf(String.class);
715 conversionService.convert(source, sourceType, targetType);
716 }
717
718 @Test
719 public void multipleCollectionTypesFromSameSourceType() throws Exception {
720 conversionService.addConverter(new MyStringToRawCollectionConverter());
721 conversionService.addConverter(new MyStringToGenericCollectionConverter());
722 conversionService.addConverter(new MyStringToStringCollectionConverter());
723 conversionService.addConverter(new MyStringToIntegerCollectionConverter());
724
725 assertEquals(Collections.singleton("testX"),
726 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
727 assertEquals(Collections.singleton(4),
728 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("integerCollection"))));
729 assertEquals(Collections.singleton(4),
730 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("rawCollection"))));
731 assertEquals(Collections.singleton(4),
732 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("genericCollection"))));
733 assertEquals(Collections.singleton(4),
734 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("rawCollection"))));
735 assertEquals(Collections.singleton("testX"),
736 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
737 }
738
739 @Test
740 public void adaptedCollectionTypesFromSameSourceType() throws Exception {
741 conversionService.addConverter(new MyStringToStringCollectionConverter());
742
743 assertEquals(Collections.singleton("testX"),
744 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
745 assertEquals(Collections.singleton("testX"),
746 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("genericCollection"))));
747 assertEquals(Collections.singleton("testX"),
748 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("rawCollection"))));
749 assertEquals(Collections.singleton("testX"),
750 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("genericCollection"))));
751 assertEquals(Collections.singleton("testX"),
752 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
753 assertEquals(Collections.singleton("testX"),
754 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("rawCollection"))));
755
756 try {
757 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("integerCollection")));
758 fail("Should have thrown ConverterNotFoundException");
759 }
760 catch (ConverterNotFoundException ex) {
761
762 }
763 }
764
765 @Test
766 public void genericCollectionAsSource() throws Exception {
767 conversionService.addConverter(new MyStringToGenericCollectionConverter());
768
769 assertEquals(Collections.singleton("testX"),
770 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
771 assertEquals(Collections.singleton("testX"),
772 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("genericCollection"))));
773 assertEquals(Collections.singleton("testX"),
774 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("rawCollection"))));
775
776
777 assertEquals(Collections.singleton("testX"),
778 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("integerCollection"))));
779 }
780
781 @Test
782 public void rawCollectionAsSource() throws Exception {
783 conversionService.addConverter(new MyStringToRawCollectionConverter());
784
785 assertEquals(Collections.singleton("testX"),
786 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
787 assertEquals(Collections.singleton("testX"),
788 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("genericCollection"))));
789 assertEquals(Collections.singleton("testX"),
790 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("rawCollection"))));
791
792
793 assertEquals(Collections.singleton("testX"),
794 conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("integerCollection"))));
795 }
796
797
798 @Retention(RetentionPolicy.RUNTIME)
799 public static @interface ExampleAnnotation {
800 }
801
802
803 private interface MyBaseInterface {
804 }
805
806
807 private interface MyInterface extends MyBaseInterface {
808 }
809
810
811 private static class MyInterfaceImplementer implements MyInterface {
812 }
813
814
815 private static class MyBaseInterfaceToStringConverter implements Converter<MyBaseInterface, String> {
816
817 @Override
818 public String convert(MyBaseInterface source) {
819 return "RESULT";
820 }
821 }
822
823
824 private static class MyStringArrayToResourceArrayConverter implements Converter<String[], Resource[]> {
825
826 @Override
827 public Resource[] convert(String[] source) {
828 Resource[] result = new Resource[source.length];
829 for (int i = 0; i < source.length; i++) {
830 result[i] = new DescriptiveResource(source[i].substring(1));
831 }
832 return result;
833 }
834 }
835
836
837 private static class MyStringArrayToIntegerArrayConverter implements Converter<String[], Integer[]> {
838
839 @Override
840 public Integer[] convert(String[] source) {
841 Integer[] result = new Integer[source.length];
842 for (int i = 0; i < source.length; i++) {
843 result[i] = Integer.parseInt(source[i].substring(1));
844 }
845 return result;
846 }
847 }
848
849
850 private static class MyStringToIntegerArrayConverter implements Converter<String, Integer[]> {
851
852 @Override
853 public Integer[] convert(String source) {
854 String[] srcArray = StringUtils.commaDelimitedListToStringArray(source);
855 Integer[] result = new Integer[srcArray.length];
856 for (int i = 0; i < srcArray.length; i++) {
857 result[i] = Integer.parseInt(srcArray[i].substring(1));
858 }
859 return result;
860 }
861 }
862
863
864 public static class WithCopyConstructor {
865
866 public WithCopyConstructor() {
867 }
868
869 public WithCopyConstructor(WithCopyConstructor value) {
870 }
871 }
872 private static class MyConditionalConverter implements Converter<String, Color>, ConditionalConverter {
873
874 private int matchAttempts = 0;
875
876 @Override
877 public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
878 matchAttempts++;
879 return false;
880 }
881
882 @Override
883 public Color convert(String source) {
884 throw new IllegalStateException();
885 }
886
887 public int getMatchAttempts() {
888 return matchAttempts;
889 }
890 }
891
892
893 private static class MyConditionalGenericConverter implements GenericConverter, ConditionalConverter {
894
895 private List<TypeDescriptor> sourceTypes = new ArrayList<TypeDescriptor>();
896
897 @Override
898 public Set<ConvertiblePair> getConvertibleTypes() {
899 return null;
900 }
901
902 @Override
903 public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
904 sourceTypes.add(sourceType);
905 return false;
906 }
907
908 @Override
909 public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
910 return null;
911 }
912
913 public List<TypeDescriptor> getSourceTypes() {
914 return sourceTypes;
915 }
916 }
917
918
919 private static class MyConditionalConverterFactory implements ConverterFactory<String, Color>, ConditionalConverter {
920
921 private MyConditionalConverter converter = new MyConditionalConverter();
922
923 private int matchAttempts = 0;
924
925 @Override
926 public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
927 matchAttempts++;
928 return true;
929 }
930
931 @Override
932 @SuppressWarnings("unchecked")
933 public <T extends Color> Converter<String, T> getConverter(Class<T> targetType) {
934 return (Converter<String, T>) converter;
935 }
936
937 public int getMatchAttempts() {
938 return matchAttempts;
939 }
940
941 public int getNestedMatchAttempts() {
942 return converter.getMatchAttempts();
943 }
944 }
945
946
947 interface MyEnumBaseInterface {
948
949 String getBaseCode();
950 }
951
952
953 interface MyEnumInterface extends MyEnumBaseInterface {
954
955 String getCode();
956 }
957
958
959 public static enum MyEnum implements MyEnumInterface {
960
961 A("1"),
962 B("2"),
963 C("3");
964
965 private String code;
966
967 MyEnum(String code) {
968 this.code = code;
969 }
970
971 @Override
972 public String getCode() {
973 return code;
974 }
975
976 @Override
977 public String getBaseCode() {
978 return "base" + code;
979 }
980 }
981
982
983 public enum EnumWithSubclass {
984
985 FIRST {
986 @Override
987 public String toString() {
988 return "1st";
989 }
990 }
991 }
992
993
994 public static class MyStringToRawCollectionConverter implements Converter<String, Collection> {
995
996 @Override
997 public Collection convert(String source) {
998 return Collections.singleton(source + "X");
999 }
1000 }
1001
1002
1003 public static class MyStringToGenericCollectionConverter implements Converter<String, Collection<?>> {
1004
1005 @Override
1006 public Collection<?> convert(String source) {
1007 return Collections.singleton(source + "X");
1008 }
1009 }
1010
1011
1012 private static class MyEnumInterfaceToStringConverter<T extends MyEnumInterface> implements Converter<T, String> {
1013
1014 @Override
1015 public String convert(T source) {
1016 return source.getCode();
1017 }
1018 }
1019
1020
1021 private static class StringToMyEnumInterfaceConverterFactory implements ConverterFactory<String, MyEnumInterface> {
1022
1023 @SuppressWarnings("unchecked")
1024 public <T extends MyEnumInterface> Converter<String, T> getConverter(Class<T> targetType) {
1025 return new StringToMyEnumInterfaceConverter(targetType);
1026 }
1027
1028 private static class StringToMyEnumInterfaceConverter<T extends Enum<?> & MyEnumInterface> implements Converter<String, T> {
1029 private final Class<T> enumType;
1030
1031 public StringToMyEnumInterfaceConverter(Class<T> enumType) {
1032 this.enumType = enumType;
1033 }
1034
1035 public T convert(String source) {
1036 for (T value : enumType.getEnumConstants()) {
1037 if (value.getCode().equals(source)) {
1038 return value;
1039 }
1040 }
1041 return null;
1042 }
1043 }
1044 }
1045
1046
1047 private static class StringToMyEnumBaseInterfaceConverterFactory implements ConverterFactory<String, MyEnumBaseInterface> {
1048
1049 @SuppressWarnings("unchecked")
1050 public <T extends MyEnumBaseInterface> Converter<String, T> getConverter(Class<T> targetType) {
1051 return new StringToMyEnumBaseInterfaceConverter(targetType);
1052 }
1053
1054 private static class StringToMyEnumBaseInterfaceConverter<T extends Enum<?> & MyEnumBaseInterface> implements Converter<String, T> {
1055
1056 private final Class<T> enumType;
1057
1058 public StringToMyEnumBaseInterfaceConverter(Class<T> enumType) {
1059 this.enumType = enumType;
1060 }
1061
1062 public T convert(String source) {
1063 for (T value : enumType.getEnumConstants()) {
1064 if (value.getBaseCode().equals(source)) {
1065 return value;
1066 }
1067 }
1068 return null;
1069 }
1070 }
1071 }
1072
1073
1074 public static class MyStringToStringCollectionConverter implements Converter<String, Collection<String>> {
1075
1076 @Override
1077 public Collection<String> convert(String source) {
1078 return Collections.singleton(source + "X");
1079 }
1080 }
1081
1082
1083 public static class MyStringToIntegerCollectionConverter implements Converter<String, Collection<Integer>> {
1084
1085 @Override
1086 public Collection<Integer> convert(String source) {
1087 return Collections.singleton(source.length());
1088 }
1089 }
1090
1091
1092 @ExampleAnnotation
1093 public String annotatedString;
1094
1095 public List<Integer> list;
1096
1097 public Map<String, Integer> map;
1098
1099 public Map<String, ?> wildcardMap;
1100
1101 public EnumSet<MyEnum> enumSet;
1102
1103 public Collection rawCollection;
1104
1105 public Collection<?> genericCollection;
1106
1107 public Collection<String> stringCollection;
1108
1109 public Collection<Integer> integerCollection;
1110
1111 }