1/*
2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.xml.internal.bind.v2.model.nav;
27
28import java.lang.reflect.Array;
29import java.lang.reflect.Field;
30import java.lang.reflect.GenericArrayType;
31import java.lang.reflect.GenericDeclaration;
32import java.lang.reflect.Method;
33import java.lang.reflect.Modifier;
34import java.lang.reflect.ParameterizedType;
35import java.lang.reflect.Type;
36import java.lang.reflect.TypeVariable;
37import java.lang.reflect.WildcardType;
38import java.security.AccessController;
39import java.security.PrivilegedAction;
40import java.util.Arrays;
41import java.util.Collection;
42
43import com.sun.xml.internal.bind.v2.runtime.Location;
44
45/**
46 * {@link Navigator} implementation for {@code java.lang.reflect}.
47 *
48 */
49/*package*/final class ReflectionNavigator implements Navigator<Type, Class, Field, Method> {
50
51//  ----------  Singleton -----------------
52    private static final ReflectionNavigator INSTANCE = new ReflectionNavigator();
53
54    /*package*/static ReflectionNavigator getInstance() {
55        return INSTANCE;
56    }
57
58    private ReflectionNavigator() {
59    }
60//  ---------------------------------------
61
62    public Class getSuperClass(Class clazz) {
63        if (clazz == Object.class) {
64            return null;
65        }
66        Class sc = clazz.getSuperclass();
67        if (sc == null) {
68            sc = Object.class;        // error recovery
69        }
70        return sc;
71    }
72
73    private static final TypeVisitor<Type, Class> baseClassFinder = new TypeVisitor<Type, Class>() {
74
75        public Type onClass(Class c, Class sup) {
76            // t is a raw type
77            if (sup == c) {
78                return sup;
79            }
80
81            Type r;
82
83            Type sc = c.getGenericSuperclass();
84            if (sc != null) {
85                r = visit(sc, sup);
86                if (r != null) {
87                    return r;
88                }
89            }
90
91            for (Type i : c.getGenericInterfaces()) {
92                r = visit(i, sup);
93                if (r != null) {
94                    return r;
95                }
96            }
97
98            return null;
99        }
100
101        public Type onParameterizdType(ParameterizedType p, Class sup) {
102            Class raw = (Class) p.getRawType();
103            if (raw == sup) {
104                // p is of the form sup<...>
105                return p;
106            } else {
107                // recursively visit super class/interfaces
108                Type r = raw.getGenericSuperclass();
109                if (r != null) {
110                    r = visit(bind(r, raw, p), sup);
111                }
112                if (r != null) {
113                    return r;
114                }
115                for (Type i : raw.getGenericInterfaces()) {
116                    r = visit(bind(i, raw, p), sup);
117                    if (r != null) {
118                        return r;
119                    }
120                }
121                return null;
122            }
123        }
124
125        public Type onGenericArray(GenericArrayType g, Class sup) {
126            // not clear what I should do here
127            return null;
128        }
129
130        public Type onVariable(TypeVariable v, Class sup) {
131            return visit(v.getBounds()[0], sup);
132        }
133
134        public Type onWildcard(WildcardType w, Class sup) {
135            // not clear what I should do here
136            return null;
137        }
138
139        /**
140         * Replaces the type variables in {@code t} by its actual arguments.
141         *
142         * @param decl
143         *      provides a list of type variables. See {@link GenericDeclaration#getTypeParameters()}
144         * @param args
145         *      actual arguments. See {@link ParameterizedType#getActualTypeArguments()}
146         */
147        private Type bind(Type t, GenericDeclaration decl, ParameterizedType args) {
148            return binder.visit(t, new BinderArg(decl, args.getActualTypeArguments()));
149        }
150    };
151
152    private static class BinderArg {
153
154        final TypeVariable[] params;
155        final Type[] args;
156
157        BinderArg(TypeVariable[] params, Type[] args) {
158            this.params = params;
159            this.args = args;
160            assert params.length == args.length;
161        }
162
163        public BinderArg(GenericDeclaration decl, Type[] args) {
164            this(decl.getTypeParameters(), args);
165        }
166
167        Type replace(TypeVariable v) {
168            for (int i = 0; i < params.length; i++) {
169                if (params[i].equals(v)) {
170                    return args[i];
171                }
172            }
173            return v;   // this is a free variable
174        }
175    }
176    private static final TypeVisitor<Type, BinderArg> binder = new TypeVisitor<Type, BinderArg>() {
177
178        public Type onClass(Class c, BinderArg args) {
179            return c;
180        }
181
182        public Type onParameterizdType(ParameterizedType p, BinderArg args) {
183            Type[] params = p.getActualTypeArguments();
184
185            boolean different = false;
186            for (int i = 0; i < params.length; i++) {
187                Type t = params[i];
188                params[i] = visit(t, args);
189                different |= t != params[i];
190            }
191
192            Type newOwner = p.getOwnerType();
193            if (newOwner != null) {
194                newOwner = visit(newOwner, args);
195            }
196            different |= p.getOwnerType() != newOwner;
197
198            if (!different) {
199                return p;
200            }
201
202            return new ParameterizedTypeImpl((Class<?>) p.getRawType(), params, newOwner);
203        }
204
205        public Type onGenericArray(GenericArrayType g, BinderArg types) {
206            Type c = visit(g.getGenericComponentType(), types);
207            if (c == g.getGenericComponentType()) {
208                return g;
209            }
210
211            return new GenericArrayTypeImpl(c);
212        }
213
214        public Type onVariable(TypeVariable v, BinderArg types) {
215            return types.replace(v);
216        }
217
218        public Type onWildcard(WildcardType w, BinderArg types) {
219            // TODO: this is probably still incorrect
220            // bind( "? extends T" ) with T= "? extends Foo" should be "? extends Foo",
221            // not "? extends (? extends Foo)"
222            Type[] lb = w.getLowerBounds();
223            Type[] ub = w.getUpperBounds();
224            boolean diff = false;
225
226            for (int i = 0; i < lb.length; i++) {
227                Type t = lb[i];
228                lb[i] = visit(t, types);
229                diff |= (t != lb[i]);
230            }
231
232            for (int i = 0; i < ub.length; i++) {
233                Type t = ub[i];
234                ub[i] = visit(t, types);
235                diff |= (t != ub[i]);
236            }
237
238            if (!diff) {
239                return w;
240            }
241
242            return new WildcardTypeImpl(lb, ub);
243        }
244    };
245
246    public Type getBaseClass(Type t, Class sup) {
247        return baseClassFinder.visit(t, sup);
248    }
249
250    public String getClassName(Class clazz) {
251        return clazz.getName();
252    }
253
254    public String getTypeName(Type type) {
255        if (type instanceof Class) {
256            Class c = (Class) type;
257            if (c.isArray()) {
258                return getTypeName(c.getComponentType()) + "[]";
259            }
260            return c.getName();
261        }
262        return type.toString();
263    }
264
265    public String getClassShortName(Class clazz) {
266        return clazz.getSimpleName();
267    }
268
269    public Collection<? extends Field> getDeclaredFields(final Class clazz) {
270        Field[] fields = AccessController.doPrivileged(new PrivilegedAction<Field[]>() {
271            @Override
272            public Field[] run() {
273                return clazz.getDeclaredFields();
274            }
275        });
276        return Arrays.asList(fields);
277    }
278
279    public Field getDeclaredField(final Class clazz, final String fieldName) {
280        return AccessController.doPrivileged(new PrivilegedAction<Field>() {
281            @Override
282            public Field run() {
283                try {
284                    return clazz.getDeclaredField(fieldName);
285                } catch (NoSuchFieldException e) {
286                    return null;
287                }
288            }
289        });
290    }
291
292    public Collection<? extends Method> getDeclaredMethods(final Class clazz) {
293        Method[] methods =
294            AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
295                @Override
296                public Method[] run() {
297                    return clazz.getDeclaredMethods();
298                }
299            });
300        return Arrays.asList(methods);
301    }
302
303    public Class getDeclaringClassForField(Field field) {
304        return field.getDeclaringClass();
305    }
306
307    public Class getDeclaringClassForMethod(Method method) {
308        return method.getDeclaringClass();
309    }
310
311    public Type getFieldType(Field field) {
312        if (field.getType().isArray()) {
313            Class c = field.getType().getComponentType();
314            if (c.isPrimitive()) {
315                return Array.newInstance(c, 0).getClass();
316            }
317        }
318        return fix(field.getGenericType());
319    }
320
321    public String getFieldName(Field field) {
322        return field.getName();
323    }
324
325    public String getMethodName(Method method) {
326        return method.getName();
327    }
328
329    public Type getReturnType(Method method) {
330        return fix(method.getGenericReturnType());
331    }
332
333    public Type[] getMethodParameters(Method method) {
334        return method.getGenericParameterTypes();
335    }
336
337    public boolean isStaticMethod(Method method) {
338        return Modifier.isStatic(method.getModifiers());
339    }
340
341    public boolean isFinalMethod(Method method) {
342        return Modifier.isFinal(method.getModifiers());
343    }
344
345    public boolean isSubClassOf(Type sub, Type sup) {
346        return erasure(sup).isAssignableFrom(erasure(sub));
347    }
348
349    public Class ref(Class c) {
350        return c;
351    }
352
353    public Class use(Class c) {
354        return c;
355    }
356
357    public Class asDecl(Type t) {
358        return erasure(t);
359    }
360
361    public Class asDecl(Class c) {
362        return c;
363    }
364    /**
365     * Implements the logic for {@link #erasure(Type)}.
366     */
367    private static final TypeVisitor<Class, Void> eraser = new TypeVisitor<Class, Void>() {
368
369        public Class onClass(Class c, Void v) {
370            return c;
371        }
372
373        public Class onParameterizdType(ParameterizedType p, Void v) {
374            // TODO: why getRawType returns Type? not Class?
375            return visit(p.getRawType(), null);
376        }
377
378        public Class onGenericArray(GenericArrayType g, Void v) {
379            return Array.newInstance(
380                    visit(g.getGenericComponentType(), null),
381                    0).getClass();
382        }
383
384        public Class onVariable(TypeVariable tv, Void v) {
385            return visit(tv.getBounds()[0], null);
386        }
387
388        public Class onWildcard(WildcardType w, Void v) {
389            return visit(w.getUpperBounds()[0], null);
390        }
391    };
392
393    /**
394     * Returns the runtime representation of the given type.
395     *
396     * This corresponds to the notion of the erasure in JSR-14.
397     *
398     * <p>
399     * Because of the difference in the way Annotation Processing and the Java reflection
400     * treats primitive type and array type, we can't define this method
401     * on {@link Navigator}.
402     *
403     * <p>
404     * It made me realize how difficult it is to define the common navigation
405     * layer for two different underlying reflection library. The other way
406     * is to throw away the entire parameterization and go to the wrapper approach.
407     */
408    public <T> Class<T> erasure(Type t) {
409        return eraser.visit(t, null);
410    }
411
412    public boolean isAbstract(Class clazz) {
413        return Modifier.isAbstract(clazz.getModifiers());
414    }
415
416    public boolean isFinal(Class clazz) {
417        return Modifier.isFinal(clazz.getModifiers());
418    }
419
420    /**
421     * Returns the {@link Type} object that represents {@code clazz<T1,T2,T3>}.
422     */
423    public Type createParameterizedType(Class rawType, Type... arguments) {
424        return new ParameterizedTypeImpl(rawType, arguments, null);
425    }
426
427    public boolean isArray(Type t) {
428        if (t instanceof Class) {
429            Class c = (Class) t;
430            return c.isArray();
431        }
432        if (t instanceof GenericArrayType) {
433            return true;
434        }
435        return false;
436    }
437
438    public boolean isArrayButNotByteArray(Type t) {
439        if (t instanceof Class) {
440            Class c = (Class) t;
441            return c.isArray() && c != byte[].class;
442        }
443        if (t instanceof GenericArrayType) {
444            t = ((GenericArrayType) t).getGenericComponentType();
445            return t != Byte.TYPE;
446        }
447        return false;
448    }
449
450    public Type getComponentType(Type t) {
451        if (t instanceof Class) {
452            Class c = (Class) t;
453            return c.getComponentType();
454        }
455        if (t instanceof GenericArrayType) {
456            return ((GenericArrayType) t).getGenericComponentType();
457        }
458
459        throw new IllegalArgumentException();
460    }
461
462    public Type getTypeArgument(Type type, int i) {
463        if (type instanceof ParameterizedType) {
464            ParameterizedType p = (ParameterizedType) type;
465            return fix(p.getActualTypeArguments()[i]);
466        } else {
467            throw new IllegalArgumentException();
468        }
469    }
470
471    public boolean isParameterizedType(Type type) {
472        return type instanceof ParameterizedType;
473    }
474
475    public boolean isPrimitive(Type type) {
476        if (type instanceof Class) {
477            Class c = (Class) type;
478            return c.isPrimitive();
479        }
480        return false;
481    }
482
483    public Type getPrimitive(Class primitiveType) {
484        assert primitiveType.isPrimitive();
485        return primitiveType;
486    }
487
488    public Location getClassLocation(final Class clazz) {
489        return new Location() {
490
491            @Override
492            public String toString() {
493                return clazz.getName();
494            }
495        };
496    }
497
498    public Location getFieldLocation(final Field field) {
499        return new Location() {
500
501            @Override
502            public String toString() {
503                return field.toString();
504            }
505        };
506    }
507
508    public Location getMethodLocation(final Method method) {
509        return new Location() {
510
511            @Override
512            public String toString() {
513                return method.toString();
514            }
515        };
516    }
517
518    public boolean hasDefaultConstructor(Class c) {
519        try {
520            c.getDeclaredConstructor();
521            return true;
522        } catch (NoSuchMethodException e) {
523            return false; // todo: do this WITHOUT exception throw
524        }
525    }
526
527    public boolean isStaticField(Field field) {
528        return Modifier.isStatic(field.getModifiers());
529    }
530
531    public boolean isPublicMethod(Method method) {
532        return Modifier.isPublic(method.getModifiers());
533    }
534
535    public boolean isPublicField(Field field) {
536        return Modifier.isPublic(field.getModifiers());
537    }
538
539    public boolean isEnum(Class c) {
540        return Enum.class.isAssignableFrom(c);
541    }
542
543    public Field[] getEnumConstants(Class clazz) {
544        try {
545            Object[] values = clazz.getEnumConstants();
546            Field[] fields = new Field[values.length];
547            for (int i = 0; i < values.length; i++) {
548                fields[i] = clazz.getField(((Enum) values[i]).name());
549            }
550            return fields;
551        } catch (NoSuchFieldException e) {
552            // impossible
553            throw new NoSuchFieldError(e.getMessage());
554        }
555    }
556
557    public Type getVoidType() {
558        return Void.class;
559    }
560
561    public String getPackageName(Class clazz) {
562        String name = clazz.getName();
563        int idx = name.lastIndexOf('.');
564        if (idx < 0) {
565            return "";
566        } else {
567            return name.substring(0, idx);
568        }
569    }
570
571    @Override
572    public Class loadObjectFactory(Class referencePoint, String pkg) {
573        ClassLoader cl = SecureLoader.getClassClassLoader(referencePoint);
574        if (cl == null)
575            cl = SecureLoader.getSystemClassLoader();
576
577        try {
578            return cl.loadClass(pkg + ".ObjectFactory");
579        } catch (ClassNotFoundException e) {
580            return null;
581        }
582    }
583
584    public boolean isBridgeMethod(Method method) {
585        return method.isBridge();
586    }
587
588    public boolean isOverriding(Method method, final Class base) {
589        // this isn't actually correct,
590        // as the JLS considers
591        // class Derived extends Base<Integer> {
592        //   Integer getX() { ... }
593        // }
594        // class Base<T> {
595        //   T getX() { ... }
596        // }
597        // to be overrided. Handling this correctly needs a careful implementation
598
599        final String name = method.getName();
600        final Class[] params = method.getParameterTypes();
601
602        return AccessController.doPrivileged(
603                new PrivilegedAction<Boolean>() {
604
605                    @Override
606                    public Boolean run() {
607                        Class clazz = base;
608                        while (clazz != null) {
609                            try {
610                                Method m = clazz.getDeclaredMethod(name, params);
611                                if (m != null) {
612                                    return Boolean.TRUE;
613                                }
614                            } catch (NoSuchMethodException ignored) {
615                                // recursively go into the base class
616                            }
617                            clazz = clazz.getSuperclass();
618                        }
619                        return Boolean.FALSE;
620                    }
621                }
622        );
623    }
624
625    public boolean isInterface(Class clazz) {
626        return clazz.isInterface();
627    }
628
629    public boolean isTransient(Field f) {
630        return Modifier.isTransient(f.getModifiers());
631    }
632
633    public boolean isInnerClass(Class clazz) {
634        return clazz.getEnclosingClass() != null && !Modifier.isStatic(clazz.getModifiers());
635    }
636
637    @Override
638    public boolean isSameType(Type t1, Type t2) {
639        return t1.equals(t2);
640    }
641
642    /**
643     * JDK 5.0 has a bug of creating {@link GenericArrayType} where it shouldn't.
644     * fix that manually to work around the problem.
645     *
646     * See bug 6202725.
647     */
648    private Type fix(Type t) {
649        if (!(t instanceof GenericArrayType)) {
650            return t;
651        }
652
653        GenericArrayType gat = (GenericArrayType) t;
654        if (gat.getGenericComponentType() instanceof Class) {
655            Class c = (Class) gat.getGenericComponentType();
656            return Array.newInstance(c, 0).getClass();
657        }
658
659        return t;
660    }
661}
662