1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.grouplens.grapht.util;
21
22 import org.apache.commons.lang3.reflect.TypeUtils;
23
24 import javax.annotation.Nonnull;
25 import javax.inject.Inject;
26 import javax.inject.Provider;
27 import java.lang.annotation.Annotation;
28 import java.lang.reflect.*;
29 import java.util.Map;
30
31
32
33
34
35
36
37 public final class Types {
38
39 private static final TypeVariable<?> PROVIDER_TYPE_VAR =Provider.class.getTypeParameters()[0];
40
41 private Types() {}
42
43 private static final Class<?>[] PRIMITIVE_TYPES = {
44 boolean.class, char.class,
45 byte.class, short.class, int.class, long.class,
46 double.class, float.class
47 };
48
49
50
51
52
53
54
55
56 public static Type parameterizedType(Class<?> type, Type... arguments) {
57 return new ParameterizedTypeImpl(type, arguments);
58 }
59
60
61
62
63
64
65
66
67
68
69 public static Type box(Type type) {
70 if (type instanceof Class) {
71 return box((Class<?>) type);
72 } else {
73 return type;
74 }
75 }
76
77
78
79
80
81
82
83
84 public static Class<?> box(Class<?> type) {
85 if (int.class.equals(type)) {
86 return Integer.class;
87 } else if (short.class.equals(type)) {
88 return Short.class;
89 } else if (byte.class.equals(type)) {
90 return Byte.class;
91 } else if (long.class.equals(type)) {
92 return Long.class;
93 } else if (boolean.class.equals(type)) {
94 return Boolean.class;
95 } else if (char.class.equals(type)) {
96 return Character.class;
97 } else if (float.class.equals(type)) {
98 return Float.class;
99 } else if (double.class.equals(type)) {
100 return Double.class;
101 } else {
102 return type;
103 }
104 }
105
106
107
108
109
110
111
112
113
114 public static Class<?> erase(Type type) {
115 if (type instanceof Class) {
116 return (Class<?>) type;
117 } else if (type instanceof ParameterizedType) {
118 ParameterizedType pt = (ParameterizedType) type;
119 Type raw = pt.getRawType();
120 try {
121 return (Class<?>) raw;
122 } catch (ClassCastException e) {
123 throw new RuntimeException("raw type not a Class", e);
124 }
125 } else {
126 throw new IllegalArgumentException();
127 }
128 }
129
130
131
132
133
134
135
136
137
138
139
140
141 public static int getTypeDistance(@Nonnull Class<?> child, @Nonnull Class<?> parent) {
142 Preconditions.notNull("child class", child);
143 Preconditions.notNull("parent class", parent);
144
145 if (child.equals(parent)) {
146
147 return 0;
148 } else if (!parent.isAssignableFrom(child)) {
149
150 throw new IllegalArgumentException("child not a subclass of parent");
151 } else if (!parent.isInterface()) {
152
153 int distance = 0;
154 Class<?> cur = child;
155 while (!cur.equals(parent)) {
156 distance++;
157 cur = cur.getSuperclass();
158 }
159 return distance;
160 } else {
161
162
163 int minDepth = Integer.MAX_VALUE;
164 Class<?> sup = child.getSuperclass();
165 if (sup != null && parent.isAssignableFrom(sup)) {
166 minDepth = getTypeDistance(sup, parent);
167 }
168 for (Class<?> iface: child.getInterfaces()) {
169 if (parent.isAssignableFrom(iface)) {
170 int d = getTypeDistance(iface, parent);
171 if (d < minDepth) {
172 minDepth = d;
173 }
174 }
175 }
176
177 return minDepth + 1;
178 }
179 }
180
181
182
183
184
185
186
187
188
189
190 public static Class<?> getProvidedType(Class<? extends Provider<?>> providerClass) {
191 com.google.common.base.Preconditions.checkArgument(Provider.class.isAssignableFrom(providerClass),
192 "class is not Provider class");
193 Map<TypeVariable<?>, Type> bindings = TypeUtils.getTypeArguments(providerClass, Provider.class);
194 Type boundType = bindings.get(PROVIDER_TYPE_VAR);
195
196 if(boundType == null || boundType instanceof TypeVariable){
197 throw new IllegalArgumentException("Class provided by " + providerClass.getName() + " is generic");
198 }
199 final Class<?> inferredType = TypeUtils.getRawType(bindings.get(PROVIDER_TYPE_VAR), null);
200 try{
201 final Class<?> observedType = providerClass.getMethod("get").getReturnType();
202 if (inferredType != null && inferredType.isAssignableFrom(observedType)) {
203 return observedType;
204 } else {
205 return inferredType;
206 }
207 } catch (NoSuchMethodException e) {
208 throw new IllegalArgumentException("Class does not implement get()", e);
209 }
210 }
211
212
213
214
215
216
217
218
219 @SuppressWarnings("unchecked")
220 public static Class<?> getProvidedType(Provider<?> provider) {
221 if (provider instanceof TypedProvider) {
222 return ((TypedProvider) provider).getProvidedType();
223 } else {
224 return getProvidedType((Class<? extends Provider<?>>) provider.getClass());
225 }
226 }
227
228
229
230
231
232
233
234
235
236 public static boolean isInstantiable(Class<?> type) {
237 if (!Modifier.isAbstract(type.getModifiers()) && !type.isInterface()) {
238
239
240
241 for (Constructor<?> c: type.getDeclaredConstructors()) {
242 if (c.getAnnotation(Inject.class) != null) {
243 return true;
244 }
245 }
246
247
248 if (type.getConstructors().length == 1
249 && type.getConstructors()[0].getParameterTypes().length == 0) {
250 return true;
251 }
252 }
253
254
255 return false;
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271 public static boolean shouldBeInstantiable(Class<?> type) {
272 return !Modifier.isAbstract(type.getModifiers()) && !type.isInterface() && !Void.class.equals(type);
273 }
274
275
276
277
278
279
280
281
282
283
284 public static boolean hasNullableAnnotation(Annotation[] annotations) {
285 for (Annotation a: annotations) {
286 if (a.annotationType().getSimpleName().equals("Nullable")) {
287 return true;
288 }
289 }
290 return false;
291 }
292
293
294
295
296
297
298 @Deprecated
299 public static ClassLoader getDefaultClassLoader() {
300 return ClassLoaders.inferDefault();
301 }
302 }