View Javadoc
1   /*
2    * Copyright 2002-2014 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.util.Arrays;
20  import java.util.Collections;
21  import java.util.EnumMap;
22  import java.util.HashMap;
23  import java.util.LinkedHashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.junit.Before;
28  import org.junit.Test;
29  
30  import org.springframework.core.convert.ConversionFailedException;
31  import org.springframework.core.convert.ConverterNotFoundException;
32  import org.springframework.core.convert.TypeDescriptor;
33  import org.springframework.util.LinkedMultiValueMap;
34  import org.springframework.util.MultiValueMap;
35  
36  import static org.hamcrest.Matchers.*;
37  import static org.junit.Assert.*;
38  
39  public class MapToMapConverterTests {
40  
41  	private GenericConversionService conversionService = new GenericConversionService();
42  
43  
44  	@Before
45  	public void setUp() {
46  		conversionService.addConverter(new MapToMapConverter(conversionService));
47  	}
48  
49  
50  	@Test
51  	public void scalarMap() throws Exception {
52  		Map<String, String> map = new HashMap<String, String>();
53  		map.put("1", "9");
54  		map.put("2", "37");
55  		TypeDescriptor sourceType = TypeDescriptor.forObject(map);
56  		TypeDescriptor targetType = new TypeDescriptor(getClass().getField("scalarMapTarget"));
57  		assertTrue(conversionService.canConvert(sourceType, targetType));
58  		try {
59  			conversionService.convert(map, sourceType, targetType);
60  		} catch (ConversionFailedException e) {
61  			assertTrue(e.getCause() instanceof ConverterNotFoundException);
62  		}
63  		conversionService.addConverterFactory(new StringToNumberConverterFactory());
64  		assertTrue(conversionService.canConvert(sourceType, targetType));
65  		@SuppressWarnings("unchecked")
66  		Map<Integer, Integer> result = (Map<Integer, Integer>) conversionService.convert(map, sourceType, targetType);
67  		assertFalse(map.equals(result));
68  		assertEquals((Integer) 9, result.get(1));
69  		assertEquals((Integer) 37, result.get(2));
70  	}
71  
72  	@Test
73  	public void scalarMapNotGenericTarget() throws Exception {
74  		Map<String, String> map = new HashMap<String, String>();
75  		map.put("1", "9");
76  		map.put("2", "37");
77  		assertTrue(conversionService.canConvert(Map.class, Map.class));
78  		assertSame(map, conversionService.convert(map, Map.class));
79  	}
80  
81  	@Test
82  	public void scalarMapNotGenericSourceField() throws Exception {
83  		Map<String, String> map = new HashMap<String, String>();
84  		map.put("1", "9");
85  		map.put("2", "37");
86  		TypeDescriptor sourceType = new TypeDescriptor(getClass().getField("notGenericMapSource"));
87  		TypeDescriptor targetType = new TypeDescriptor(getClass().getField("scalarMapTarget"));
88  		assertTrue(conversionService.canConvert(sourceType, targetType));
89  		try {
90  			conversionService.convert(map, sourceType, targetType);
91  		} catch (ConversionFailedException e) {
92  			assertTrue(e.getCause() instanceof ConverterNotFoundException);
93  		}
94  		conversionService.addConverterFactory(new StringToNumberConverterFactory());
95  		assertTrue(conversionService.canConvert(sourceType, targetType));
96  		@SuppressWarnings("unchecked")
97  		Map<Integer, Integer> result = (Map<Integer, Integer>) conversionService.convert(map, sourceType, targetType);
98  		assertFalse(map.equals(result));
99  		assertEquals((Integer) 9, result.get(1));
100 		assertEquals((Integer) 37, result.get(2));
101 	}
102 
103 	@Test
104 	public void collectionMap() throws Exception {
105 		Map<String, List<String>> map = new HashMap<String, List<String>>();
106 		map.put("1", Arrays.asList("9", "12"));
107 		map.put("2", Arrays.asList("37", "23"));
108 		TypeDescriptor sourceType = TypeDescriptor.forObject(map);
109 		TypeDescriptor targetType = new TypeDescriptor(getClass().getField("collectionMapTarget"));
110 		assertTrue(conversionService.canConvert(sourceType, targetType));
111 		try {
112 			conversionService.convert(map, sourceType, targetType);
113 		} catch (ConversionFailedException e) {
114 			assertTrue(e.getCause() instanceof ConverterNotFoundException);
115 		}
116 		conversionService.addConverter(new CollectionToCollectionConverter(conversionService));
117 		conversionService.addConverterFactory(new StringToNumberConverterFactory());
118 		assertTrue(conversionService.canConvert(sourceType, targetType));
119 		@SuppressWarnings("unchecked")
120 		Map<Integer, List<Integer>> result = (Map<Integer, List<Integer>>) conversionService.convert(map, sourceType, targetType);
121 		assertFalse(map.equals(result));
122 		assertEquals(Arrays.asList(9, 12), result.get(1));
123 		assertEquals(Arrays.asList(37, 23), result.get(2));
124 	}
125 
126 	@Test
127 	public void collectionMapSourceTarget() throws Exception {
128 		Map<String, List<String>> map = new HashMap<String, List<String>>();
129 		map.put("1", Arrays.asList("9", "12"));
130 		map.put("2", Arrays.asList("37", "23"));
131 		TypeDescriptor sourceType = new TypeDescriptor(getClass().getField("sourceCollectionMapTarget"));
132 		TypeDescriptor targetType = new TypeDescriptor(getClass().getField("collectionMapTarget"));
133 		assertFalse(conversionService.canConvert(sourceType, targetType));
134 		try {
135 			conversionService.convert(map, sourceType, targetType);
136 			fail("Should have failed");
137 		}
138 		catch (ConverterNotFoundException ex) {
139 			// expected
140 		}
141 		conversionService.addConverter(new CollectionToCollectionConverter(conversionService));
142 		conversionService.addConverterFactory(new StringToNumberConverterFactory());
143 		assertTrue(conversionService.canConvert(sourceType, targetType));
144 		@SuppressWarnings("unchecked")
145 		Map<Integer, List<Integer>> result = (Map<Integer, List<Integer>>) conversionService.convert(map, sourceType, targetType);
146 		assertFalse(map.equals(result));
147 		assertEquals(Arrays.asList(9, 12), result.get(1));
148 		assertEquals(Arrays.asList(37, 23), result.get(2));
149 	}
150 
151 	@Test
152 	public void collectionMapNotGenericTarget() throws Exception {
153 		Map<String, List<String>> map = new HashMap<String, List<String>>();
154 		map.put("1", Arrays.asList("9", "12"));
155 		map.put("2", Arrays.asList("37", "23"));
156 		assertTrue(conversionService.canConvert(Map.class, Map.class));
157 		assertSame(map, conversionService.convert(map, Map.class));
158 	}
159 
160 	@Test
161 	public void collectionMapNotGenericTargetCollectionToObjectInteraction() throws Exception {
162 		Map<String, List<String>> map = new HashMap<String, List<String>>();
163 		map.put("1", Arrays.asList("9", "12"));
164 		map.put("2", Arrays.asList("37", "23"));
165 		conversionService.addConverter(new CollectionToCollectionConverter(conversionService));
166 		conversionService.addConverter(new CollectionToObjectConverter(conversionService));
167 		assertTrue(conversionService.canConvert(Map.class, Map.class));
168 		assertSame(map, conversionService.convert(map, Map.class));
169 	}
170 
171 	@Test
172 	public void emptyMap() throws Exception {
173 		Map<String, String> map = new HashMap<String, String>();
174 		TypeDescriptor sourceType = TypeDescriptor.forObject(map);
175 		TypeDescriptor targetType = new TypeDescriptor(getClass().getField("emptyMapTarget"));
176 		assertTrue(conversionService.canConvert(sourceType, targetType));
177 		assertSame(map, conversionService.convert(map, sourceType, targetType));
178 	}
179 
180 	@Test
181 	public void emptyMapNoTargetGenericInfo() throws Exception {
182 		Map<String, String> map = new HashMap<String, String>();
183 		assertTrue(conversionService.canConvert(Map.class, Map.class));
184 		assertSame(map, conversionService.convert(map, Map.class));
185 	}
186 
187 	@Test
188 	public void emptyMapDifferentTargetImplType() throws Exception {
189 		Map<String, String> map = new HashMap<String, String>();
190 		TypeDescriptor sourceType = TypeDescriptor.forObject(map);
191 		TypeDescriptor targetType = new TypeDescriptor(getClass().getField("emptyMapDifferentTarget"));
192 		assertTrue(conversionService.canConvert(sourceType, targetType));
193 		@SuppressWarnings("unchecked")
194 		LinkedHashMap<String, String> result = (LinkedHashMap<String, String>) conversionService.convert(map, sourceType, targetType);
195 		assertEquals(map, result);
196 		assertEquals(LinkedHashMap.class, result.getClass());
197 	}
198 
199 	@Test
200 	public void noDefaultConstructorCopyNotRequired() throws Exception {
201 		// SPR-9284
202 		NoDefaultConstructorMap<String, Integer> map = new NoDefaultConstructorMap<String,Integer>(
203 				Collections.<String, Integer> singletonMap("1", 1));
204 		TypeDescriptor sourceType = TypeDescriptor.map(NoDefaultConstructorMap.class,
205 				TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class));
206 		TypeDescriptor targetType = TypeDescriptor.map(NoDefaultConstructorMap.class,
207 				TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class));
208 		assertTrue(conversionService.canConvert(sourceType, targetType));
209 		@SuppressWarnings("unchecked")
210 		Map<String, Integer> result = (Map<String, Integer>) conversionService.convert(map, sourceType, targetType);
211 		assertEquals(map, result);
212 		assertEquals(NoDefaultConstructorMap.class, result.getClass());
213 	}
214 
215 	@Test
216 	@SuppressWarnings("unchecked")
217 	public void multiValueMapToMultiValueMap() throws Exception {
218 		DefaultConversionService.addDefaultConverters(conversionService);
219 		MultiValueMap<String, Integer> source = new LinkedMultiValueMap<String, Integer>();
220 		source.put("a", Arrays.asList(1, 2, 3));
221 		source.put("b", Arrays.asList(4, 5, 6));
222 		TypeDescriptor targetType = new TypeDescriptor(getClass().getField("multiValueMapTarget"));
223 		MultiValueMap<String, String> converted = (MultiValueMap<String, String>) conversionService.convert(source, targetType);
224 		assertThat(converted.size(), equalTo(2));
225 		assertThat(converted.get("a"), equalTo(Arrays.asList("1", "2", "3")));
226 		assertThat(converted.get("b"), equalTo(Arrays.asList("4", "5", "6")));
227 	}
228 
229 	@Test
230 	@SuppressWarnings("unchecked")
231 	public void mapToMultiValueMap() throws Exception {
232 		DefaultConversionService.addDefaultConverters(conversionService);
233 		Map<String, Integer> source = new HashMap<String, Integer>();
234 		source.put("a", 1);
235 		source.put("b", 2);
236 		TypeDescriptor targetType = new TypeDescriptor(getClass().getField("multiValueMapTarget"));
237 		MultiValueMap<String, String> converted = (MultiValueMap<String, String>) conversionService.convert(source, targetType);
238 		assertThat(converted.size(), equalTo(2));
239 		assertThat(converted.get("a"), equalTo(Arrays.asList("1")));
240 		assertThat(converted.get("b"), equalTo(Arrays.asList("2")));
241 	}
242 
243 	@Test
244 	public void testStringToEnumMap() throws Exception {
245 		conversionService.addConverterFactory(new StringToEnumConverterFactory());
246 		Map<String, Integer> source = new HashMap<String, Integer>();
247 		source.put("A", 1);
248 		source.put("C", 2);
249 		EnumMap<MyEnum, Integer> result = new EnumMap<MyEnum, Integer>(MyEnum.class);
250 		result.put(MyEnum.A, 1);
251 		result.put(MyEnum.C, 2);
252 		assertEquals(result,
253 				conversionService.convert(source, TypeDescriptor.forObject(source), new TypeDescriptor(getClass().getField("enumMap"))));
254 	}
255 
256 
257 	@SuppressWarnings("serial")
258 	public static class NoDefaultConstructorMap<K, V> extends HashMap<K, V> {
259 
260 		public NoDefaultConstructorMap(Map<? extends K, ? extends V> map) {
261 			super(map);
262 		}
263 	}
264 
265 
266 	public static enum MyEnum {A, B, C}
267 
268 
269 	public Map<Integer, Integer> scalarMapTarget;
270 
271 	public Map<Integer, List<Integer>> collectionMapTarget;
272 
273 	public Map<String, List<String>> sourceCollectionMapTarget;
274 
275 	public Map<String, String> emptyMapTarget;
276 
277 	public LinkedHashMap<String, String> emptyMapDifferentTarget;
278 
279 	public MultiValueMap<String, String> multiValueMapTarget;
280 
281 	public Map notGenericMapSource;
282 
283 	public EnumMap<MyEnum, Integer> enumMap;
284 
285 }