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;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.EnumMap;
22  import java.util.EnumSet;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.LinkedHashMap;
26  import java.util.LinkedHashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.NavigableMap;
30  import java.util.NavigableSet;
31  import java.util.Set;
32  import java.util.SortedMap;
33  import java.util.SortedSet;
34  import java.util.TreeMap;
35  import java.util.TreeSet;
36  
37  import org.junit.Test;
38  import org.springframework.util.LinkedMultiValueMap;
39  import org.springframework.util.MultiValueMap;
40  
41  import static org.hamcrest.CoreMatchers.*;
42  import static org.junit.Assert.*;
43  import static org.springframework.core.CollectionFactory.*;
44  
45  /**
46   * Unit tests for {@link CollectionFactory}.
47   *
48   * @author Oliver Gierke
49   * @author Sam Brannen
50   * @since 4.1.4
51   */
52  public class CollectionFactoryTests {
53  
54  	/**
55  	 * The test demonstrates that the generics-based API for
56  	 * {@link CollectionFactory#createApproximateCollection(Object, int)}
57  	 * is not type-safe.
58  	 * <p>Specifically, the parameterized type {@code E} is not bound to
59  	 * the type of elements contained in the {@code collection} argument
60  	 * passed to {@code createApproximateCollection()}. Thus casting the
61  	 * value returned by {@link EnumSet#copyOf(EnumSet)} to
62  	 * {@code (Collection<E>)} cannot guarantee that the returned collection
63  	 * actually contains elements of type {@code E}.
64  	 */
65  	@Test
66  	public void createApproximateCollectionIsNotTypeSafeForEnumSet() {
67  		Collection<Integer> ints = createApproximateCollection(EnumSet.of(Color.BLUE), 3);
68  
69  		// Use a try-catch block to ensure that the exception is thrown as a result of the
70  		// next line and not as a result of the previous line.
71  		try {
72  			// Note that ints is of type Collection<Integer>, but the collection returned
73  			// by createApproximateCollection() is of type Collection<Color>. Thus, 42
74  			// cannot be cast to a Color.
75  			ints.add(42);
76  			fail("Should have thrown a ClassCastException");
77  		}
78  		catch (ClassCastException e) {
79  			/* expected */
80  		}
81  	}
82  
83  	@Test
84  	public void createCollectionIsNotTypeSafeForEnumSet() {
85  		Collection<Integer> ints = createCollection(EnumSet.class, Color.class, 3);
86  
87  		// Use a try-catch block to ensure that the exception is thrown as a result of the
88  		// next line and not as a result of the previous line.
89  		try {
90  			// Note that ints is of type Collection<Integer>, but the collection returned
91  			// by createCollection() is of type Collection<Color>. Thus, 42 cannot be cast
92  			// to a Color.
93  			ints.add(42);
94  			fail("Should have thrown a ClassCastException");
95  		}
96  		catch (ClassCastException e) {
97  			/* expected */
98  		}
99  	}
100 
101 	/**
102 	 * The test demonstrates that the generics-based API for
103 	 * {@link CollectionFactory#createApproximateMap(Object, int)}
104 	 * is not type-safe.
105 	 * <p>The reasoning is similar that described in
106 	 * {@link #createApproximateCollectionIsNotTypeSafe()}.
107 	 */
108 	@Test
109 	public void createApproximateMapIsNotTypeSafeForEnumMap() {
110 		EnumMap<Color, Integer> enumMap = new EnumMap<>(Color.class);
111 		enumMap.put(Color.RED, 1);
112 		enumMap.put(Color.BLUE, 2);
113 		Map<String, Integer> map = createApproximateMap(enumMap, 3);
114 
115 		// Use a try-catch block to ensure that the exception is thrown as a result of the
116 		// next line and not as a result of the previous line.
117 		try {
118 			// Note that the 'map' key must be of type String, but the keys in the map
119 			// returned by createApproximateMap() are of type Color. Thus "foo" cannot be
120 			// cast to a Color.
121 			map.put("foo", 1);
122 			fail("Should have thrown a ClassCastException");
123 		}
124 		catch (ClassCastException e) {
125 			/* expected */
126 		}
127 	}
128 
129 	@Test
130 	public void createMapIsNotTypeSafeForEnumMap() {
131 		Map<String, Integer> map = createMap(EnumMap.class, Color.class, 3);
132 
133 		// Use a try-catch block to ensure that the exception is thrown as a result of the
134 		// next line and not as a result of the previous line.
135 		try {
136 			// Note that the 'map' key must be of type String, but the keys in the map
137 			// returned by createMap() are of type Color. Thus "foo" cannot be cast to a
138 			// Color.
139 			map.put("foo", 1);
140 			fail("Should have thrown a ClassCastException");
141 		}
142 		catch (ClassCastException e) {
143 			/* expected */
144 		}
145 	}
146 
147 	@Test
148 	public void createMapIsNotTypeSafeForLinkedMultiValueMap() {
149 		Map<String, Integer> map = createMap(MultiValueMap.class, null, 3);
150 
151 		// Use a try-catch block to ensure that the exception is thrown as a result of the
152 		// next line and not as a result of the previous line.
153 		try {
154 			// Note: 'map' values must be of type Integer, but the values in the map
155 			// returned by createMap() are of type java.util.List. Thus 1 cannot be
156 			// cast to a List.
157 			map.put("foo", 1);
158 			fail("Should have thrown a ClassCastException");
159 		}
160 		catch (ClassCastException e) {
161 			/* expected */
162 		}
163 	}
164 
165 	@Test
166 	public void createApproximateCollectionFromEmptyHashSet() {
167 		Collection<String> set = createApproximateCollection(new HashSet<String>(), 2);
168 		assertThat(set.size(), is(0));
169 	}
170 
171 	@Test
172 	public void createApproximateCollectionFromNonEmptyHashSet() {
173 		HashSet<String> hashSet = new HashSet<String>();
174 		hashSet.add("foo");
175 		Collection<String> set = createApproximateCollection(hashSet, 2);
176 		assertThat(set.size(), is(0));
177 	}
178 
179 	@Test
180 	public void createApproximateCollectionFromEmptyEnumSet() {
181 		Collection<Color> colors = createApproximateCollection(EnumSet.noneOf(Color.class), 2);
182 		assertThat(colors.size(), is(0));
183 	}
184 
185 	@Test
186 	public void createApproximateCollectionFromNonEmptyEnumSet() {
187 		Collection<Color> colors = createApproximateCollection(EnumSet.of(Color.BLUE), 2);
188 		assertThat(colors.size(), is(0));
189 	}
190 
191 	@Test
192 	public void createApproximateMapFromEmptyHashMap() {
193 		Map<String, String> map = createApproximateMap(new HashMap<String, String>(), 2);
194 		assertThat(map.size(), is(0));
195 	}
196 
197 	@Test
198 	public void createApproximateMapFromNonEmptyHashMap() {
199 		Map<String, String> hashMap = new HashMap<String, String>();
200 		hashMap.put("foo", "bar");
201 		Map<String, String> map = createApproximateMap(hashMap, 2);
202 		assertThat(map.size(), is(0));
203 	}
204 
205 	@Test
206 	public void createApproximateMapFromEmptyEnumMap() {
207 		Map<Color, String> colors = createApproximateMap(new EnumMap<Color, String>(Color.class), 2);
208 		assertThat(colors.size(), is(0));
209 	}
210 
211 	@Test
212 	public void createApproximateMapFromNonEmptyEnumMap() {
213 		EnumMap<Color, String> enumMap = new EnumMap<Color, String>(Color.class);
214 		enumMap.put(Color.BLUE, "blue");
215 		Map<Color, String> colors = createApproximateMap(enumMap, 2);
216 		assertThat(colors.size(), is(0));
217 	}
218 
219 	@Test
220 	public void createsCollectionsCorrectly() {
221 		// interfaces
222 		assertThat(createCollection(List.class, 0), is(instanceOf(ArrayList.class)));
223 		assertThat(createCollection(Set.class, 0), is(instanceOf(LinkedHashSet.class)));
224 		assertThat(createCollection(Collection.class, 0), is(instanceOf(LinkedHashSet.class)));
225 		assertThat(createCollection(SortedSet.class, 0), is(instanceOf(TreeSet.class)));
226 		assertThat(createCollection(NavigableSet.class, 0), is(instanceOf(TreeSet.class)));
227 
228 		assertThat(createCollection(List.class, String.class, 0), is(instanceOf(ArrayList.class)));
229 		assertThat(createCollection(Set.class, String.class, 0), is(instanceOf(LinkedHashSet.class)));
230 		assertThat(createCollection(Collection.class, String.class, 0), is(instanceOf(LinkedHashSet.class)));
231 		assertThat(createCollection(SortedSet.class, String.class, 0), is(instanceOf(TreeSet.class)));
232 		assertThat(createCollection(NavigableSet.class, String.class, 0), is(instanceOf(TreeSet.class)));
233 
234 		// concrete types
235 		assertThat(createCollection(HashSet.class, 0), is(instanceOf(HashSet.class)));
236 		assertThat(createCollection(HashSet.class, String.class, 0), is(instanceOf(HashSet.class)));
237 	}
238 
239 	@Test
240 	public void createsEnumSet() {
241 		assertThat(createCollection(EnumSet.class, Color.class, 0), is(instanceOf(EnumSet.class)));
242 	}
243 
244 	@Test(expected = IllegalArgumentException.class)
245 	public void rejectsInvalidElementTypeForEnumSet() {
246 		createCollection(EnumSet.class, Object.class, 0);
247 	}
248 
249 	@Test(expected = IllegalArgumentException.class)
250 	public void rejectsNullElementTypeForEnumSet() {
251 		createCollection(EnumSet.class, null, 0);
252 	}
253 
254 	@Test(expected = IllegalArgumentException.class)
255 	public void rejectsNullCollectionType() {
256 		createCollection(null, Object.class, 0);
257 	}
258 
259 	@Test
260 	public void createsMapsCorrectly() {
261 		// interfaces
262 		assertThat(createMap(Map.class, 0), is(instanceOf(LinkedHashMap.class)));
263 		assertThat(createMap(SortedMap.class, 0), is(instanceOf(TreeMap.class)));
264 		assertThat(createMap(NavigableMap.class, 0), is(instanceOf(TreeMap.class)));
265 		assertThat(createMap(MultiValueMap.class, 0), is(instanceOf(LinkedMultiValueMap.class)));
266 
267 		assertThat(createMap(Map.class, String.class, 0), is(instanceOf(LinkedHashMap.class)));
268 		assertThat(createMap(SortedMap.class, String.class, 0), is(instanceOf(TreeMap.class)));
269 		assertThat(createMap(NavigableMap.class, String.class, 0), is(instanceOf(TreeMap.class)));
270 		assertThat(createMap(MultiValueMap.class, String.class, 0), is(instanceOf(LinkedMultiValueMap.class)));
271 
272 		// concrete types
273 		assertThat(createMap(HashMap.class, 0), is(instanceOf(HashMap.class)));
274 
275 		assertThat(createMap(HashMap.class, String.class, 0), is(instanceOf(HashMap.class)));
276 	}
277 
278 	@Test
279 	public void createsEnumMap() {
280 		assertThat(createMap(EnumMap.class, Color.class, 0), is(instanceOf(EnumMap.class)));
281 	}
282 
283 	@Test(expected = IllegalArgumentException.class)
284 	public void rejectsInvalidKeyTypeForEnumMap() {
285 		createMap(EnumMap.class, Object.class, 0);
286 	}
287 
288 	@Test(expected = IllegalArgumentException.class)
289 	public void rejectsNullKeyTypeForEnumMap() {
290 		createMap(EnumMap.class, null, 0);
291 	}
292 
293 	@Test(expected = IllegalArgumentException.class)
294 	public void rejectsNullMapType() {
295 		createMap(null, Object.class, 0);
296 	}
297 
298 
299 	static enum Color {
300 		RED, BLUE;
301 	}
302 }