1 /*
2  * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Distribution License v. 1.0, which is available at
6  * http://www.eclipse.org/org/documents/edl-v10.php.
7  *
8  * SPDX-License-Identifier: BSD-3-Clause
9  */

10
11 package com.sun.xml.bind.v2.model.nav;
12
13 import java.lang.reflect.Array;
14 import java.lang.reflect.Field;
15 import java.lang.reflect.GenericArrayType;
16 import java.lang.reflect.GenericDeclaration;
17 import java.lang.reflect.Method;
18 import java.lang.reflect.Modifier;
19 import java.lang.reflect.ParameterizedType;
20 import java.lang.reflect.Type;
21 import java.lang.reflect.TypeVariable;
22 import java.lang.reflect.WildcardType;
23 import java.security.AccessController;
24 import java.security.PrivilegedAction;
25 import java.util.Arrays;
26 import java.util.Collection;
27
28 import com.sun.xml.bind.v2.runtime.Location;
29
30 /**
31  * {@link Navigator} implementation for {@code java.lang.reflect}.
32  *
33  */

34 /*package*/final class ReflectionNavigator implements Navigator<Type, Class, Field, Method> {
35
36 //  ----------  Singleton -----------------
37     private static final ReflectionNavigator INSTANCE = new ReflectionNavigator();
38
39     /*package*/static ReflectionNavigator getInstance() {
40         return INSTANCE;
41     }
42
43     private ReflectionNavigator() {
44     }
45 //  ---------------------------------------
46
47     public Class getSuperClass(Class clazz) {
48         if (clazz == Object.class) {
49             return null;
50         }
51         Class sc = clazz.getSuperclass();
52         if (sc == null) {
53             sc = Object.class;        // error recovery
54         }
55         return sc;
56     }
57
58     private static final TypeVisitor<Type, Class> baseClassFinder = new TypeVisitor<Type, Class>() {
59
60         public Type onClass(Class c, Class sup) {
61             // t is a raw type
62             if (sup == c) {
63                 return sup;
64             }
65
66             Type r;
67
68             Type sc = c.getGenericSuperclass();
69             if (sc != null) {
70                 r = visit(sc, sup);
71                 if (r != null) {
72                     return r;
73                 }
74             }
75
76             for (Type i : c.getGenericInterfaces()) {
77                 r = visit(i, sup);
78                 if (r != null) {
79                     return r;
80                 }
81             }
82
83             return null;
84         }
85
86         public Type onParameterizdType(ParameterizedType p, Class sup) {
87             Class raw = (Class) p.getRawType();
88             if (raw == sup) {
89                 // p is of the form sup<...>
90                 return p;
91             } else {
92                 // recursively visit super class/interfaces
93                 Type r = raw.getGenericSuperclass();
94                 if (r != null) {
95                     r = visit(bind(r, raw, p), sup);
96                 }
97                 if (r != null) {
98                     return r;
99                 }
100                 for (Type i : raw.getGenericInterfaces()) {
101                     r = visit(bind(i, raw, p), sup);
102                     if (r != null) {
103                         return r;
104                     }
105                 }
106                 return null;
107             }
108         }
109
110         public Type onGenericArray(GenericArrayType g, Class sup) {
111             // not clear what I should do here
112             return null;
113         }
114
115         public Type onVariable(TypeVariable v, Class sup) {
116             return visit(v.getBounds()[0], sup);
117         }
118
119         public Type onWildcard(WildcardType w, Class sup) {
120             // not clear what I should do here
121             return null;
122         }
123
124         /**
125          * Replaces the type variables in {@code t} by its actual arguments.
126          *
127          * @param decl
128          *      provides a list of type variables. See {@link GenericDeclaration#getTypeParameters()}
129          * @param args
130          *      actual arguments. See {@link ParameterizedType#getActualTypeArguments()}
131          */

132         private Type bind(Type t, GenericDeclaration decl, ParameterizedType args) {
133             return binder.visit(t, new BinderArg(decl, args.getActualTypeArguments()));
134         }
135     };
136
137     private static class BinderArg {
138
139         final TypeVariable[] params;
140         final Type[] args;
141
142         BinderArg(TypeVariable[] params, Type[] args) {
143             this.params = params;
144             this.args = args;
145             assert params.length == args.length;
146         }
147
148         public BinderArg(GenericDeclaration decl, Type[] args) {
149             this(decl.getTypeParameters(), args);
150         }
151
152         Type replace(TypeVariable v) {
153             for (int i = 0; i < params.length; i++) {
154                 if (params[i].equals(v)) {
155                     return args[i];
156                 }
157             }
158             return v;   // this is a free variable
159         }
160     }
161     private static final TypeVisitor<Type, BinderArg> binder = new TypeVisitor<Type, BinderArg>() {
162
163         public Type onClass(Class c, BinderArg args) {
164             return c;
165         }
166
167         public Type onParameterizdType(ParameterizedType p, BinderArg args) {
168             Type[] params = p.getActualTypeArguments();
169
170             boolean different = false;
171             for (int i = 0; i < params.length; i++) {
172                 Type t = params[i];
173                 params[i] = visit(t, args);
174                 different |= t != params[i];
175             }
176
177             Type newOwner = p.getOwnerType();
178             if (newOwner != null) {
179                 newOwner = visit(newOwner, args);
180             }
181             different |= p.getOwnerType() != newOwner;
182
183             if (!different) {
184                 return p;
185             }
186
187             return new ParameterizedTypeImpl((Class<?>) p.getRawType(), params, newOwner);
188         }
189
190         public Type onGenericArray(GenericArrayType g, BinderArg types) {
191             Type c = visit(g.getGenericComponentType(), types);
192             if (c == g.getGenericComponentType()) {
193                 return g;
194             }
195
196             return new GenericArrayTypeImpl(c);
197         }
198
199         public Type onVariable(TypeVariable v, BinderArg types) {
200             return types.replace(v);
201         }
202
203         public Type onWildcard(WildcardType w, BinderArg types) {
204             // TODO: this is probably still incorrect
205             // bind( "? extends T" ) with T= "? extends Foo" should be "? extends Foo",
206             // not "? extends (? extends Foo)"
207             Type[] lb = w.getLowerBounds();
208             Type[] ub = w.getUpperBounds();
209             boolean diff = false;
210
211             for (int i = 0; i < lb.length; i++) {
212                 Type t = lb[i];
213                 lb[i] = visit(t, types);
214                 diff |= (t != lb[i]);
215             }
216
217             for (int i = 0; i < ub.length; i++) {
218                 Type t = ub[i];
219                 ub[i] = visit(t, types);
220                 diff |= (t != ub[i]);
221             }
222
223             if (!diff) {
224                 return w;
225             }
226
227             return new WildcardTypeImpl(lb, ub);
228         }
229     };
230
231     public Type getBaseClass(Type t, Class sup) {
232         return baseClassFinder.visit(t, sup);
233     }
234
235     public String getClassName(Class clazz) {
236         return clazz.getName();
237     }
238
239     public String getTypeName(Type type) {
240         if (type instanceof Class) {
241             Class c = (Class) type;
242             if (c.isArray()) {
243                 return getTypeName(c.getComponentType()) + "[]";
244             }
245             return c.getName();
246         }
247         return type.toString();
248     }
249
250     public String getClassShortName(Class clazz) {
251         return clazz.getSimpleName();
252     }
253
254     public Collection<? extends Field> getDeclaredFields(final Class clazz) {
255         Field[] fields = AccessController.doPrivileged(new PrivilegedAction<Field[]>() {
256             @Override
257             public Field[] run() {
258                 return clazz.getDeclaredFields();
259             }
260         });
261         return Arrays.asList(fields);
262     }
263
264     public Field getDeclaredField(final Class clazz, final String fieldName) {
265         return AccessController.doPrivileged(new PrivilegedAction<Field>() {
266             @Override
267             public Field run() {
268                 try {
269                     return clazz.getDeclaredField(fieldName);
270                 } catch (NoSuchFieldException e) {
271                     return null;
272                 }
273             }
274         });
275     }
276
277     public Collection<? extends Method> getDeclaredMethods(final Class clazz) {
278         Method[] methods =
279             AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
280                 @Override
281                 public Method[] run() {
282                     return clazz.getDeclaredMethods();
283                 }
284             });
285         return Arrays.asList(methods);
286     }
287
288     public Class getDeclaringClassForField(Field field) {
289         return field.getDeclaringClass();
290     }
291
292     public Class getDeclaringClassForMethod(Method method) {
293         return method.getDeclaringClass();
294     }
295
296     public Type getFieldType(Field field) {
297         if (field.getType().isArray()) {
298             Class c = field.getType().getComponentType();
299             if (c.isPrimitive()) {
300                 return Array.newInstance(c, 0).getClass();
301             }
302         }
303         return fix(field.getGenericType());
304     }
305
306     public String getFieldName(Field field) {
307         return field.getName();
308     }
309
310     public String getMethodName(Method method) {
311         return method.getName();
312     }
313
314     public Type getReturnType(Method method) {
315         return fix(method.getGenericReturnType());
316     }
317
318     public Type[] getMethodParameters(Method method) {
319         return method.getGenericParameterTypes();
320     }
321
322     public boolean isStaticMethod(Method method) {
323         return Modifier.isStatic(method.getModifiers());
324     }
325
326     public boolean isFinalMethod(Method method) {
327         return Modifier.isFinal(method.getModifiers());
328     }
329
330     public boolean isSubClassOf(Type sub, Type sup) {
331         return erasure(sup).isAssignableFrom(erasure(sub));
332     }
333
334     public Class ref(Class c) {
335         return c;
336     }
337
338     public Class use(Class c) {
339         return c;
340     }
341
342     public Class asDecl(Type t) {
343         return erasure(t);
344     }
345
346     public Class asDecl(Class c) {
347         return c;
348     }
349     /**
350      * Implements the logic for {@link #erasure(Type)}.
351      */

352     private static final TypeVisitor<Class, Void> eraser = new TypeVisitor<Class, Void>() {
353
354         public Class onClass(Class c, Void v) {
355             return c;
356         }
357
358         public Class onParameterizdType(ParameterizedType p, Void v) {
359             // TODO: why getRawType returns Type? not Class?
360             return visit(p.getRawType(), null);
361         }
362
363         public Class onGenericArray(GenericArrayType g, Void v) {
364             return Array.newInstance(
365                     visit(g.getGenericComponentType(), null),
366                     0).getClass();
367         }
368
369         public Class onVariable(TypeVariable tv, Void v) {
370             return visit(tv.getBounds()[0], null);
371         }
372
373         public Class onWildcard(WildcardType w, Void v) {
374             return visit(w.getUpperBounds()[0], null);
375         }
376     };
377
378     /**
379      * Returns the runtime representation of the given type.
380      *
381      * This corresponds to the notion of the erasure in JSR-14.
382      *
383      * <p>
384      * Because of the difference in the way Annotation Processing and the Java reflection
385      * treats primitive type and array type, we can't define this method
386      * on {@link Navigator}.
387      *
388      * <p>
389      * It made me realize how difficult it is to define the common navigation
390      * layer for two different underlying reflection library. The other way
391      * is to throw away the entire parameterization and go to the wrapper approach.
392      */

393     public <T> Class<T> erasure(Type t) {
394         return eraser.visit(t, null);
395     }
396
397     public boolean isAbstract(Class clazz) {
398         return Modifier.isAbstract(clazz.getModifiers());
399     }
400
401     public boolean isFinal(Class clazz) {
402         return Modifier.isFinal(clazz.getModifiers());
403     }
404
405     /**
406      * Returns the {@link Type} object that represents {@code clazz<T1,T2,T3>}.
407      */

408     public Type createParameterizedType(Class rawType, Type... arguments) {
409         return new ParameterizedTypeImpl(rawType, arguments, null);
410     }
411
412     public boolean isArray(Type t) {
413         if (t instanceof Class) {
414             Class c = (Class) t;
415             return c.isArray();
416         }
417         if (t instanceof GenericArrayType) {
418             return true;
419         }
420         return false;
421     }
422
423     public boolean isArrayButNotByteArray(Type t) {
424         if (t instanceof Class) {
425             Class c = (Class) t;
426             return c.isArray() && c != byte[].class;
427         }
428         if (t instanceof GenericArrayType) {
429             t = ((GenericArrayType) t).getGenericComponentType();
430             return t != Byte.TYPE;
431         }
432         return false;
433     }
434
435     public Type getComponentType(Type t) {
436         if (t instanceof Class) {
437             Class c = (Class) t;
438             return c.getComponentType();
439         }
440         if (t instanceof GenericArrayType) {
441             return ((GenericArrayType) t).getGenericComponentType();
442         }
443
444         throw new IllegalArgumentException();
445     }
446
447     public Type getTypeArgument(Type type, int i) {
448         if (type instanceof ParameterizedType) {
449             ParameterizedType p = (ParameterizedType) type;
450             return fix(p.getActualTypeArguments()[i]);
451         } else {
452             throw new IllegalArgumentException();
453         }
454     }
455
456     public boolean isParameterizedType(Type type) {
457         return type instanceof ParameterizedType;
458     }
459
460     public boolean isPrimitive(Type type) {
461         if (type instanceof Class) {
462             Class c = (Class) type;
463             return c.isPrimitive();
464         }
465         return false;
466     }
467
468     public Type getPrimitive(Class primitiveType) {
469         assert primitiveType.isPrimitive();
470         return primitiveType;
471     }
472
473     public Location getClassLocation(final Class clazz) {
474         return new Location() {
475
476             @Override
477             public String toString() {
478                 return clazz.getName();
479             }
480         };
481     }
482
483     public Location getFieldLocation(final Field field) {
484         return new Location() {
485
486             @Override
487             public String toString() {
488                 return field.toString();
489             }
490         };
491     }
492
493     public Location getMethodLocation(final Method method) {
494         return new Location() {
495
496             @Override
497             public String toString() {
498                 return method.toString();
499             }
500         };
501     }
502
503     public boolean hasDefaultConstructor(Class c) {
504         try {
505             c.getDeclaredConstructor();
506             return true;
507         } catch (NoSuchMethodException e) {
508             return false// todo: do this WITHOUT exception throw
509         }
510     }
511
512     public boolean isStaticField(Field field) {
513         return Modifier.isStatic(field.getModifiers());
514     }
515
516     public boolean isPublicMethod(Method method) {
517         return Modifier.isPublic(method.getModifiers());
518     }
519
520     public boolean isPublicField(Field field) {
521         return Modifier.isPublic(field.getModifiers());
522     }
523
524     public boolean isEnum(Class c) {
525         return Enum.class.isAssignableFrom(c);
526     }
527
528     public Field[] getEnumConstants(Class clazz) {
529         try {
530             Object[] values = clazz.getEnumConstants();
531             Field[] fields = new Field[values.length];
532             for (int i = 0; i < values.length; i++) {
533                 fields[i] = clazz.getField(((Enum) values[i]).name());
534             }
535             return fields;
536         } catch (NoSuchFieldException e) {
537             // impossible
538             throw new NoSuchFieldError(e.getMessage());
539         }
540     }
541
542     public Type getVoidType() {
543         return Void.class;
544     }
545
546     public String getPackageName(Class clazz) {
547         String name = clazz.getName();
548         int idx = name.lastIndexOf('.');
549         if (idx < 0) {
550             return "";
551         } else {
552             return name.substring(0, idx);
553         }
554     }
555
556     @Override
557     public Class loadObjectFactory(Class referencePoint, String pkg) {
558         ClassLoader cl = SecureLoader.getClassClassLoader(referencePoint);
559         if (cl == null)
560             cl = SecureLoader.getSystemClassLoader();
561
562         try {
563             return cl.loadClass(pkg + ".ObjectFactory");
564         } catch (ClassNotFoundException e) {
565             return null;
566         }
567     }
568
569     public boolean isBridgeMethod(Method method) {
570         return method.isBridge();
571     }
572
573     public boolean isOverriding(Method method, final Class base) {
574         // this isn't actually correct,
575         // as the JLS considers
576         // class Derived extends Base<Integer> {
577         //   Integer getX() { ... }
578         // }
579         // class Base<T> {
580         //   T getX() { ... }
581         // }
582         // to be overrided. Handling this correctly needs a careful implementation
583
584         final String name = method.getName();
585         final Class[] params = method.getParameterTypes();
586
587         return AccessController.doPrivileged(
588                 new PrivilegedAction<Boolean>() {
589
590                     @Override
591                     public Boolean run() {
592                         Class clazz = base;
593                         while (clazz != null) {
594                             try {
595                                 Method m = clazz.getDeclaredMethod(name, params);
596                                 if (m != null) {
597                                     return Boolean.TRUE;
598                                 }
599                             } catch (NoSuchMethodException ignored) {
600                                 // recursively go into the base class
601                             }
602                             clazz = clazz.getSuperclass();
603                         }
604                         return Boolean.FALSE;
605                     }
606                 }
607         );
608     }
609
610     public boolean isInterface(Class clazz) {
611         return clazz.isInterface();
612     }
613
614     public boolean isTransient(Field f) {
615         return Modifier.isTransient(f.getModifiers());
616     }
617
618     public boolean isInnerClass(Class clazz) {
619         return clazz.getEnclosingClass() != null && !Modifier.isStatic(clazz.getModifiers());
620     }
621
622     @Override
623     public boolean isSameType(Type t1, Type t2) {
624         return t1.equals(t2);
625     }
626
627     /**
628      * JDK 5.0 has a bug of creating {@link GenericArrayType} where it shouldn't.
629      * fix that manually to work around the problem.
630      *
631      * See bug 6202725.
632      */

633     private Type fix(Type t) {
634         if (!(t instanceof GenericArrayType)) {
635             return t;
636         }
637
638         GenericArrayType gat = (GenericArrayType) t;
639         if (gat.getGenericComponentType() instanceof Class) {
640             Class c = (Class) gat.getGenericComponentType();
641             return Array.newInstance(c, 0).getClass();
642         }
643
644         return t;
645     }
646 }
647