1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.websocket.common.util;
20
21 import java.lang.reflect.Constructor;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Modifier;
24 import java.lang.reflect.ParameterizedType;
25 import java.lang.reflect.Type;
26 import java.lang.reflect.TypeVariable;
27
28 public class ReflectUtils
29 {
30 private static class GenericRef
31 {
32
33 private final Class<?> baseClass;
34
35 private final Class<?> ifaceClass;
36
37
38 Class<?> genericClass;
39
40
41 public Type genericType;
42 private int genericIndex;
43
44 public GenericRef(final Class<?> baseClass, final Class<?> ifaceClass)
45 {
46 this.baseClass = baseClass;
47 this.ifaceClass = ifaceClass;
48 }
49
50 public boolean needsUnwrap()
51 {
52 return (genericClass == null) && (genericType != null) && (genericType instanceof TypeVariable<?>);
53 }
54
55 public void setGenericFromType(Type type, int index)
56 {
57
58 this.genericType = type;
59 this.genericIndex = index;
60 if (type instanceof Class)
61 {
62 this.genericClass = (Class<?>)type;
63 }
64 }
65
66 @Override
67 public String toString()
68 {
69 StringBuilder builder = new StringBuilder();
70 builder.append("GenericRef [baseClass=");
71 builder.append(baseClass);
72 builder.append(", ifaceClass=");
73 builder.append(ifaceClass);
74 builder.append(", genericType=");
75 builder.append(genericType);
76 builder.append(", genericClass=");
77 builder.append(genericClass);
78 builder.append("]");
79 return builder.toString();
80 }
81 }
82
83 private static StringBuilder appendTypeName(StringBuilder sb, Type type, boolean ellipses)
84 {
85 if (type instanceof Class<?>)
86 {
87 Class<?> ctype = (Class<?>)type;
88 if (ctype.isArray())
89 {
90 try
91 {
92 int dimensions = 0;
93 while (ctype.isArray())
94 {
95 dimensions++;
96 ctype = ctype.getComponentType();
97 }
98 sb.append(ctype.getName());
99 for (int i = 0; i < dimensions; i++)
100 {
101 if (ellipses)
102 {
103 sb.append("...");
104 }
105 else
106 {
107 sb.append("[]");
108 }
109 }
110 return sb;
111 }
112 catch (Throwable ignore)
113 {
114
115 }
116 }
117
118 sb.append(ctype.getName());
119 }
120 else
121 {
122 sb.append(type.toString());
123 }
124
125 return sb;
126 }
127
128
129
130
131
132
133
134
135
136
137 public static Class<?> findGenericClassFor(Class<?> baseClass, Class<?> ifaceClass)
138 {
139 GenericRef ref = new GenericRef(baseClass,ifaceClass);
140 if (resolveGenericRef(ref,baseClass))
141 {
142
143 return ref.genericClass;
144 }
145
146
147 return null;
148 }
149
150 private static int findTypeParameterIndex(Class<?> clazz, TypeVariable<?> needVar)
151 {
152
153 TypeVariable<?> params[] = clazz.getTypeParameters();
154 for (int i = 0; i < params.length; i++)
155 {
156 if (params[i].getName().equals(needVar.getName()))
157 {
158
159 return i;
160 }
161 }
162
163 return -1;
164 }
165
166 public static boolean isDefaultConstructable(Class<?> clazz)
167 {
168 int mods = clazz.getModifiers();
169 if (Modifier.isAbstract(mods) || !Modifier.isPublic(mods))
170 {
171
172 return false;
173 }
174
175 Class<?>[] noargs = new Class<?>[0];
176 try
177 {
178
179 Constructor<?> constructor = clazz.getConstructor(noargs);
180
181 return Modifier.isPublic(constructor.getModifiers());
182 }
183 catch (NoSuchMethodException | SecurityException e)
184 {
185 return false;
186 }
187 }
188
189 private static boolean resolveGenericRef(GenericRef ref, Class<?> clazz, Type type)
190 {
191 if (type instanceof Class)
192 {
193 if (type == ref.ifaceClass)
194 {
195
196
197 ref.setGenericFromType(type,0);
198 return true;
199 }
200 else
201 {
202
203 return resolveGenericRef(ref,type);
204 }
205 }
206
207 if (type instanceof ParameterizedType)
208 {
209 ParameterizedType ptype = (ParameterizedType)type;
210 Type rawType = ptype.getRawType();
211 if (rawType == ref.ifaceClass)
212 {
213
214
215 ref.setGenericFromType(ptype.getActualTypeArguments()[0],0);
216 return true;
217 }
218 else
219 {
220
221 return resolveGenericRef(ref,rawType);
222 }
223 }
224 return false;
225 }
226
227 private static boolean resolveGenericRef(GenericRef ref, Type type)
228 {
229 if ((type == null) || (type == Object.class))
230 {
231 return false;
232 }
233
234 if (type instanceof Class)
235 {
236 Class<?> clazz = (Class<?>)type;
237
238
239 if (clazz.getName().matches("^javax*\\..*"))
240 {
241 return false;
242 }
243
244 Type ifaces[] = clazz.getGenericInterfaces();
245 for (Type iface : ifaces)
246 {
247
248 if (resolveGenericRef(ref,clazz,iface))
249 {
250 if (ref.needsUnwrap())
251 {
252
253 TypeVariable<?> needVar = (TypeVariable<?>)ref.genericType;
254
255
256
257 int typeParamIdx = findTypeParameterIndex(clazz,needVar);
258
259
260 if (typeParamIdx >= 0)
261 {
262
263
264 TypeVariable<?> params[] = clazz.getTypeParameters();
265 if (params.length >= typeParamIdx)
266 {
267 ref.setGenericFromType(params[typeParamIdx],typeParamIdx);
268 }
269 }
270 else if (iface instanceof ParameterizedType)
271 {
272
273 Type arg = ((ParameterizedType)iface).getActualTypeArguments()[ref.genericIndex];
274 ref.setGenericFromType(arg,ref.genericIndex);
275 }
276 }
277 return true;
278 }
279 }
280
281 type = clazz.getGenericSuperclass();
282 return resolveGenericRef(ref,type);
283 }
284
285 if (type instanceof ParameterizedType)
286 {
287 ParameterizedType ptype = (ParameterizedType)type;
288 Class<?> rawClass = (Class<?>)ptype.getRawType();
289 if (resolveGenericRef(ref,rawClass))
290 {
291 if (ref.needsUnwrap())
292 {
293
294 TypeVariable<?> needVar = (TypeVariable<?>)ref.genericType;
295
296 int typeParamIdx = findTypeParameterIndex(rawClass,needVar);
297
298
299 Type arg = ptype.getActualTypeArguments()[typeParamIdx];
300 ref.setGenericFromType(arg,typeParamIdx);
301 return true;
302 }
303 }
304 }
305
306 return false;
307 }
308
309 public static String toShortName(Type type)
310 {
311 if (type == null)
312 {
313 return "<null>";
314 }
315
316 if (type instanceof Class)
317 {
318 String name = ((Class<?>)type).getName();
319 return trimClassName(name);
320 }
321
322 if (type instanceof ParameterizedType)
323 {
324 ParameterizedType ptype = (ParameterizedType)type;
325 StringBuilder str = new StringBuilder();
326 str.append(trimClassName(((Class<?>)ptype.getRawType()).getName()));
327 str.append("<");
328 Type args[] = ptype.getActualTypeArguments();
329 for (int i = 0; i < args.length; i++)
330 {
331 if (i > 0)
332 {
333 str.append(",");
334 }
335 str.append(args[i]);
336 }
337 str.append(">");
338 return str.toString();
339 }
340
341 return type.toString();
342 }
343
344 public static String toString(Class<?> pojo, Method method)
345 {
346 StringBuilder str = new StringBuilder();
347
348
349 int mod = method.getModifiers() & Modifier.methodModifiers();
350 if (mod != 0)
351 {
352 str.append(Modifier.toString(mod)).append(' ');
353 }
354
355
356 Type retType = method.getGenericReturnType();
357 appendTypeName(str,retType,false).append(' ');
358
359
360 str.append(pojo.getName());
361 str.append("#");
362
363
364 str.append(method.getName());
365
366
367 str.append('(');
368 Type[] params = method.getGenericParameterTypes();
369 for (int j = 0; j < params.length; j++)
370 {
371 boolean ellipses = method.isVarArgs() && (j == (params.length - 1));
372 appendTypeName(str,params[j],ellipses);
373 if (j < (params.length - 1))
374 {
375 str.append(", ");
376 }
377 }
378 str.append(')');
379
380
381 return str.toString();
382 }
383
384 public static String trimClassName(String name)
385 {
386 int idx = name.lastIndexOf('.');
387 name = name.substring(idx + 1);
388 idx = name.lastIndexOf('$');
389 if (idx >= 0)
390 {
391 name = name.substring(idx + 1);
392 }
393 return name;
394 }
395 }