View Javadoc
1   /*-
2    * #%L
3    * io.earcam.instrumental.reflect
4    * %%
5    * Copyright (C) 2018 earcam
6    * %%
7    * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
8    * 
9    * You <b>must</b> choose to accept, in full - any individual or combination of 
10   * the following licenses:
11   * <ul>
12   * 	<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
13   * 	<li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
14   * 	<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
15   * 	<li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
16   * </ul>
17   * #L%
18   */
19  package io.earcam.instrumental.reflect;
20  
21  import static java.util.Arrays.asList;
22  import static java.util.Collections.unmodifiableMap;
23  import static java.util.Collections.unmodifiableSet;
24  import static java.util.stream.Collectors.toMap;
25  import static java.util.stream.Collectors.toSet;
26  
27  import java.lang.reflect.Type;
28  import java.util.Arrays;
29  import java.util.Collection;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Objects;
34  import java.util.Set;
35  import java.util.function.Predicate;
36  import java.util.stream.Collectors;
37  import java.util.stream.Stream;
38  
39  import io.earcam.unexceptional.Exceptional;
40  
41  /**
42   * <p>
43   * Types class.
44   * </p>
45   *
46   */
47  public final class Types {
48  
49  	private static final ClassLoader CLASS_LOADER = Types.class.getClassLoader();
50  
51  	/**
52  	 * {@link java.lang.Class#isPrimitive()}
53  	 */
54  	private static final Set<String> PRIMITIVES = namesOf(
55  			short.class, int.class, long.class, float.class, double.class, char.class, byte.class, boolean.class, void.class);
56  
57  	private static final Set<String> WRAPPERS = namesOf(
58  			Short.class, Integer.class, Long.class, Float.class, Double.class, Character.class, Byte.class, Boolean.class, Void.class);
59  
60  	private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE;
61  	private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER;
62  	static {
63  		Map<Class<?>, Class<?>> map = new HashMap<>(8);
64  		map.put(Boolean.class, boolean.class);
65  		map.put(Byte.class, byte.class);
66  		map.put(Character.class, char.class);
67  		map.put(Double.class, double.class);
68  		map.put(Float.class, float.class);
69  		map.put(Integer.class, int.class);
70  		map.put(Long.class, long.class);
71  		map.put(Short.class, short.class);
72  		map.put(Void.class, void.class);
73  		WRAPPER_TO_PRIMITIVE = unmodifiableMap(map);
74  		PRIMITIVE_TO_WRAPPER = unmodifiableMap(WRAPPER_TO_PRIMITIVE.entrySet().stream().collect(toMap(Map.Entry::getValue, Map.Entry::getKey)));
75  	}
76  
77  
78  	private Types()
79  	{}
80  
81  
82  	private static Set<String> namesOf(Class<?>... items)
83  	{
84  		return unmodifiableSet(asList(items).stream()
85  				.map(Class::getCanonicalName)
86  				.collect(toSet()));
87  	}
88  
89  
90  	/**
91  	 * <p>
92  	 * wrapperToPrimitive.
93  	 * </p>
94  	 *
95  	 * @param type a {@link Class} object.
96  	 * @return a {@link Class} object.
97  	 */
98  	public static Class<?> wrapperToPrimitive(Class<?> type)
99  	{
100 		return WRAPPER_TO_PRIMITIVE.get(type);
101 	}
102 
103 
104 	/**
105 	 * <p>
106 	 * primitiveToWrapper.
107 	 * </p>
108 	 *
109 	 * @param type a {@link Class} object.
110 	 * @return a {@link Class} object.
111 	 */
112 	public static Class<?> primitiveToWrapper(Class<?> type)
113 	{
114 		return PRIMITIVE_TO_WRAPPER.get(type);
115 	}
116 
117 
118 	/**
119 	 * <p>
120 	 * isPrimitive.
121 	 * </p>
122 	 *
123 	 * @param type a {@link java.lang.reflect.Type} object.
124 	 * @return a boolean.
125 	 */
126 	public static boolean isPrimitive(Type type)
127 	{
128 
129 		Class<?> clazz = getClass(type);
130 		return clazz.isPrimitive();
131 	}
132 
133 
134 	/**
135 	 * <p>
136 	 * isPrimitive.
137 	 * </p>
138 	 *
139 	 * @param name a {@link java.lang.String} object.
140 	 * @return a boolean.
141 	 */
142 	public static boolean isPrimitive(String name)
143 	{
144 		return PRIMITIVES.contains(name);
145 	}
146 
147 
148 	/**
149 	 * <p>
150 	 * isPrimitiveWrapper.
151 	 * </p>
152 	 *
153 	 * @param type a {@link java.lang.reflect.Type} object.
154 	 * @return a boolean.
155 	 */
156 	public static boolean isPrimitiveWrapper(Type type)
157 	{
158 		return isPrimitiveWrapper(type.getTypeName());
159 	}
160 
161 
162 	/**
163 	 * <p>
164 	 * isPrimitiveWrapper.
165 	 * </p>
166 	 *
167 	 * @param name a {@link java.lang.String} object.
168 	 * @return a boolean.
169 	 */
170 	public static boolean isPrimitiveWrapper(String name)
171 	{
172 		return WRAPPERS.contains(name);
173 	}
174 
175 
176 	/**
177 	 * <p>
178 	 * isPrimitiveArray.
179 	 * </p>
180 	 *
181 	 * @param type a {@link java.lang.reflect.Type} object.
182 	 * @return a boolean.
183 	 */
184 	public static boolean isPrimitiveArray(Type type)
185 	{
186 		Class<?> clazz = getClass(type);
187 		return clazz.isArray() && isPrimitive(clazz.getComponentType());
188 	}
189 
190 
191 	/**
192 	 * <p>
193 	 * isInterface.
194 	 * </p>
195 	 *
196 	 * @param type a {@link java.lang.reflect.Type} object.
197 	 * @return a boolean.
198 	 */
199 	public static boolean isInterface(Type type)
200 	{
201 		Class<?> clazz = getClass(type);
202 		return clazz.isInterface() && !clazz.isAnnotation();
203 	}
204 
205 
206 	/**
207 	 * <p>
208 	 * isInterface.
209 	 * </p>
210 	 *
211 	 * @param name a {@link java.lang.String} object.
212 	 * @return a boolean.
213 	 */
214 	public static boolean isInterface(String name)
215 	{
216 		return isInterface(getClass(name, CLASS_LOADER));
217 	}
218 
219 
220 	/**
221 	 * <p>
222 	 * isClass.
223 	 * </p>
224 	 *
225 	 * @param type a {@link java.lang.reflect.Type} object.
226 	 * @return a boolean.
227 	 */
228 	public static boolean isClass(Type type)
229 	{
230 		Class<?> clazz = getClass(type);
231 		return clazz.getSuperclass() != null || Object.class.equals(clazz);
232 	}
233 
234 
235 	/**
236 	 * <p>
237 	 * requireClass.
238 	 * </p>
239 	 *
240 	 * @param type a {@link java.lang.reflect.Type} object.
241 	 */
242 	public static void requireClass(Type type)
243 	{
244 		Objects.requireNonNull(type, "type cannot be null");
245 		if(!isClass(type)) {
246 			throw new IllegalArgumentException("type '" + type.getTypeName() + "' is not a class");
247 		}
248 	}
249 
250 
251 	/**
252 	 * <p>
253 	 * isClass.
254 	 * </p>
255 	 *
256 	 * @param name a {@link java.lang.String} object.
257 	 * @return a boolean.
258 	 */
259 	public static boolean isClass(String name)
260 	{
261 		return isClass(getClass(name, CLASS_LOADER));
262 	}
263 
264 
265 	/**
266 	 * <p>
267 	 * getClass.
268 	 * </p>
269 	 *
270 	 * @param type a {@link java.lang.reflect.Type} object.
271 	 * @return a {@link Class} object.
272 	 */
273 	public static Class<?> getClass(Type type)
274 	{
275 		return getClass(type, CLASS_LOADER);
276 	}
277 
278 
279 	/**
280 	 * <p>
281 	 * getClass.
282 	 * </p>
283 	 *
284 	 * @param type a {@link java.lang.reflect.Type} object.
285 	 * @param classLoader a {@link java.lang.ClassLoader} object.
286 	 * @return a {@link Class} object.
287 	 */
288 	public static Class<?> getClass(Type type, ClassLoader classLoader)
289 	{
290 		return type instanceof Class ? ((Class<?>) type) : getClass(type.getTypeName(), classLoader);
291 	}
292 
293 
294 	/**
295 	 * <p>
296 	 * getClass.
297 	 * </p>
298 	 *
299 	 * @param typeName a {@link java.lang.String} object.
300 	 * @param classLoader a {@link java.lang.ClassLoader} object.
301 	 * @return a {@link Class} object.
302 	 */
303 	public static Class<?> getClass(String typeName, ClassLoader classLoader)
304 	{
305 		return Exceptional.apply(classLoader::loadClass, typeName);
306 	}
307 
308 
309 	/**
310 	 * <p>
311 	 * requireInterface.
312 	 * </p>
313 	 *
314 	 * @param type a {@link java.lang.reflect.Type} object.
315 	 */
316 	public static void requireInterface(Type type)
317 	{
318 		Objects.requireNonNull(type, "type cannot be null");
319 		if(!isInterface(type)) {
320 			throw new IllegalArgumentException("type '" + type.getTypeName() + "' is not an interface");
321 		}
322 	}
323 
324 
325 	/**
326 	 * <p>
327 	 * implementsAll.
328 	 * </p>
329 	 *
330 	 * @param type a {@link Class} object.
331 	 * @param interfaces a {@link Class} object.
332 	 * @return a boolean.
333 	 */
334 	public static boolean implementsAll(Class<?> type, Class<?>... interfaces)
335 	{
336 		return implementsAll(type, Arrays.asList(interfaces));
337 
338 	}
339 
340 
341 	/**
342 	 * <p>
343 	 * Returns {@code true} IFF {@code type} implements all {@code interfaces}.
344 	 * </p>
345 	 *
346 	 * @param type a {@link Class} object.
347 	 * @param interfaces a {@link Collection} object.
348 	 * @return a boolean.
349 	 */
350 	public static boolean implementsAll(Class<?> type, Collection<Class<?>> interfaces)
351 	{
352 		List<Class<?>> implemented = allInterfacesOf(type).collect(Collectors.toList());
353 		Predicate<? super Class<?>> contained = implemented::contains;
354 		return !(implemented.isEmpty() || interfaces.stream().anyMatch(contained.negate()));
355 	}
356 
357 
358 	/**
359 	 * <p>
360 	 * Return all the interfaces of a given class.
361 	 * </p>
362 	 *
363 	 * @param type a {@link Class}.
364 	 * @return a {@link Stream} consisting of all interfaces of {@code type}.
365 	 */
366 	public static Stream<Class<?>> allInterfacesOf(Class<?> type)
367 	{
368 		Stream<Class<?>> interfaces = Arrays.stream(type.getInterfaces());
369 		return type.getSuperclass() == null ? interfaces : Stream.concat(interfaces, allInterfacesOf(type.getSuperclass()));
370 	}
371 
372 
373 	/**
374 	 * <p>
375 	 * Return {@code true} IFF the given {@code type} extends from the {@code superType}.
376 	 * </p>
377 	 *
378 	 * @param type a {@link Class} to check.
379 	 * @param superType a {@link Class} to check against.
380 	 * @return {@code true} IFF {@code type extends superType}.
381 	 */
382 	public static boolean extendsFrom(Class<?> type, Class<?> superType)
383 	{
384 		Stream<Class<?>> supers = allSuperTypesOf(type);
385 		return supers.anyMatch(Predicate.isEqual(superType));
386 	}
387 
388 
389 	/**
390 	 * <p>
391 	 * Return all the super-types of a given class.
392 	 * </p>
393 	 *
394 	 * @param type a {@link Class} object.
395 	 * @return a {@link Stream} object.
396 	 */
397 	public static Stream<Class<?>> allSuperTypesOf(Class<?> type)
398 	{
399 		Stream<Class<?>> supers = Stream.of(type);
400 		return type.getSuperclass() == null ? supers : Stream.concat(supers, allSuperTypesOf(type.getSuperclass()));
401 	}
402 }