325N/A/*
325N/A * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.xml.internal.bind.v2.model.nav;
325N/A
325N/Aimport java.lang.reflect.Array;
325N/Aimport java.lang.reflect.Field;
325N/Aimport java.lang.reflect.GenericArrayType;
325N/Aimport java.lang.reflect.GenericDeclaration;
325N/Aimport java.lang.reflect.Method;
325N/Aimport java.lang.reflect.Modifier;
325N/Aimport java.lang.reflect.ParameterizedType;
325N/Aimport java.lang.reflect.Type;
325N/Aimport java.lang.reflect.TypeVariable;
325N/Aimport java.lang.reflect.WildcardType;
325N/Aimport java.util.Arrays;
325N/Aimport java.util.Collection;
325N/A
325N/Aimport com.sun.xml.internal.bind.v2.runtime.Location;
325N/A
325N/A/**
325N/A * {@link Navigator} implementation for {@code java.lang.reflect}.
325N/A *
325N/A */
325N/Apublic final class ReflectionNavigator implements Navigator<Type, Class, Field, Method> {
325N/A
325N/A /**
325N/A * Singleton.
325N/A *
325N/A * Use {@link Navigator#REFLECTION}
325N/A */
325N/A ReflectionNavigator() {
325N/A }
325N/A
325N/A public Class getSuperClass(Class clazz) {
325N/A if (clazz == Object.class) {
325N/A return null;
325N/A }
325N/A Class sc = clazz.getSuperclass();
325N/A if (sc == null) {
325N/A sc = Object.class; // error recovery
325N/A }
325N/A return sc;
325N/A }
325N/A private static final TypeVisitor<Type, Class> baseClassFinder = new TypeVisitor<Type, Class>() {
325N/A
325N/A public Type onClass(Class c, Class sup) {
325N/A // t is a raw type
325N/A if (sup == c) {
325N/A return sup;
325N/A }
325N/A
325N/A Type r;
325N/A
325N/A Type sc = c.getGenericSuperclass();
325N/A if (sc != null) {
325N/A r = visit(sc, sup);
325N/A if (r != null) {
325N/A return r;
325N/A }
325N/A }
325N/A
325N/A for (Type i : c.getGenericInterfaces()) {
325N/A r = visit(i, sup);
325N/A if (r != null) {
325N/A return r;
325N/A }
325N/A }
325N/A
325N/A return null;
325N/A }
325N/A
325N/A public Type onParameterizdType(ParameterizedType p, Class sup) {
325N/A Class raw = (Class) p.getRawType();
325N/A if (raw == sup) {
325N/A // p is of the form sup<...>
325N/A return p;
325N/A } else {
325N/A // recursively visit super class/interfaces
325N/A Type r = raw.getGenericSuperclass();
325N/A if (r != null) {
325N/A r = visit(bind(r, raw, p), sup);
325N/A }
325N/A if (r != null) {
325N/A return r;
325N/A }
325N/A for (Type i : raw.getGenericInterfaces()) {
325N/A r = visit(bind(i, raw, p), sup);
325N/A if (r != null) {
325N/A return r;
325N/A }
325N/A }
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A public Type onGenericArray(GenericArrayType g, Class sup) {
325N/A // not clear what I should do here
325N/A return null;
325N/A }
325N/A
325N/A public Type onVariable(TypeVariable v, Class sup) {
325N/A return visit(v.getBounds()[0], sup);
325N/A }
325N/A
325N/A public Type onWildcard(WildcardType w, Class sup) {
325N/A // not clear what I should do here
325N/A return null;
325N/A }
325N/A
325N/A /**
325N/A * Replaces the type variables in {@code t} by its actual arguments.
325N/A *
325N/A * @param decl
325N/A * provides a list of type variables. See {@link GenericDeclaration#getTypeParameters()}
325N/A * @param args
325N/A * actual arguments. See {@link ParameterizedType#getActualTypeArguments()}
325N/A */
325N/A private Type bind(Type t, GenericDeclaration decl, ParameterizedType args) {
325N/A return binder.visit(t, new BinderArg(decl, args.getActualTypeArguments()));
325N/A }
325N/A };
325N/A
325N/A private static class BinderArg {
325N/A
325N/A final TypeVariable[] params;
325N/A final Type[] args;
325N/A
325N/A BinderArg(TypeVariable[] params, Type[] args) {
325N/A this.params = params;
325N/A this.args = args;
325N/A assert params.length == args.length;
325N/A }
325N/A
325N/A public BinderArg(GenericDeclaration decl, Type[] args) {
325N/A this(decl.getTypeParameters(), args);
325N/A }
325N/A
325N/A Type replace(TypeVariable v) {
325N/A for (int i = 0; i < params.length; i++) {
325N/A if (params[i].equals(v)) {
325N/A return args[i];
325N/A }
325N/A }
325N/A return v; // this is a free variable
325N/A }
325N/A }
325N/A private static final TypeVisitor<Type, BinderArg> binder = new TypeVisitor<Type, BinderArg>() {
325N/A
325N/A public Type onClass(Class c, BinderArg args) {
325N/A return c;
325N/A }
325N/A
325N/A public Type onParameterizdType(ParameterizedType p, BinderArg args) {
325N/A Type[] params = p.getActualTypeArguments();
325N/A
325N/A boolean different = false;
325N/A for (int i = 0; i < params.length; i++) {
325N/A Type t = params[i];
325N/A params[i] = visit(t, args);
325N/A different |= t != params[i];
325N/A }
325N/A
325N/A Type newOwner = p.getOwnerType();
325N/A if (newOwner != null) {
325N/A newOwner = visit(newOwner, args);
325N/A }
325N/A different |= p.getOwnerType() != newOwner;
325N/A
325N/A if (!different) {
325N/A return p;
325N/A }
325N/A
325N/A return new ParameterizedTypeImpl((Class<?>) p.getRawType(), params, newOwner);
325N/A }
325N/A
325N/A public Type onGenericArray(GenericArrayType g, BinderArg types) {
325N/A Type c = visit(g.getGenericComponentType(), types);
325N/A if (c == g.getGenericComponentType()) {
325N/A return g;
325N/A }
325N/A
325N/A return new GenericArrayTypeImpl(c);
325N/A }
325N/A
325N/A public Type onVariable(TypeVariable v, BinderArg types) {
325N/A return types.replace(v);
325N/A }
325N/A
325N/A public Type onWildcard(WildcardType w, BinderArg types) {
325N/A // TODO: this is probably still incorrect
325N/A // bind( "? extends T" ) with T= "? extends Foo" should be "? extends Foo",
325N/A // not "? extends (? extends Foo)"
325N/A Type[] lb = w.getLowerBounds();
325N/A Type[] ub = w.getUpperBounds();
325N/A boolean diff = false;
325N/A
325N/A for (int i = 0; i < lb.length; i++) {
325N/A Type t = lb[i];
325N/A lb[i] = visit(t, types);
325N/A diff |= (t != lb[i]);
325N/A }
325N/A
325N/A for (int i = 0; i < ub.length; i++) {
325N/A Type t = ub[i];
325N/A ub[i] = visit(t, types);
325N/A diff |= (t != ub[i]);
325N/A }
325N/A
325N/A if (!diff) {
325N/A return w;
325N/A }
325N/A
325N/A return new WildcardTypeImpl(lb, ub);
325N/A }
325N/A };
325N/A
325N/A public Type getBaseClass(Type t, Class sup) {
325N/A return baseClassFinder.visit(t, sup);
325N/A }
325N/A
325N/A public String getClassName(Class clazz) {
325N/A return clazz.getName();
325N/A }
325N/A
325N/A public String getTypeName(Type type) {
325N/A if (type instanceof Class) {
325N/A Class c = (Class) type;
325N/A if (c.isArray()) {
325N/A return getTypeName(c.getComponentType()) + "[]";
325N/A }
325N/A return c.getName();
325N/A }
325N/A return type.toString();
325N/A }
325N/A
325N/A public String getClassShortName(Class clazz) {
325N/A return clazz.getSimpleName();
325N/A }
325N/A
325N/A public Collection<? extends Field> getDeclaredFields(Class clazz) {
325N/A return Arrays.asList(clazz.getDeclaredFields());
325N/A }
325N/A
325N/A public Field getDeclaredField(Class clazz, String fieldName) {
325N/A try {
325N/A return clazz.getDeclaredField(fieldName);
325N/A } catch (NoSuchFieldException e) {
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A public Collection<? extends Method> getDeclaredMethods(Class clazz) {
325N/A return Arrays.asList(clazz.getDeclaredMethods());
325N/A }
325N/A
325N/A public Class getDeclaringClassForField(Field field) {
325N/A return field.getDeclaringClass();
325N/A }
325N/A
325N/A public Class getDeclaringClassForMethod(Method method) {
325N/A return method.getDeclaringClass();
325N/A }
325N/A
325N/A public Type getFieldType(Field field) {
325N/A if (field.getType().isArray()) {
325N/A Class c = field.getType().getComponentType();
325N/A if (c.isPrimitive()) {
325N/A return Array.newInstance(c, 0).getClass();
325N/A }
325N/A }
325N/A return fix(field.getGenericType());
325N/A }
325N/A
325N/A public String getFieldName(Field field) {
325N/A return field.getName();
325N/A }
325N/A
325N/A public String getMethodName(Method method) {
325N/A return method.getName();
325N/A }
325N/A
325N/A public Type getReturnType(Method method) {
325N/A return fix(method.getGenericReturnType());
325N/A }
325N/A
325N/A public Type[] getMethodParameters(Method method) {
325N/A return method.getGenericParameterTypes();
325N/A }
325N/A
325N/A public boolean isStaticMethod(Method method) {
325N/A return Modifier.isStatic(method.getModifiers());
325N/A }
325N/A
325N/A public boolean isFinalMethod(Method method) {
325N/A return Modifier.isFinal(method.getModifiers());
325N/A }
325N/A
325N/A public boolean isSubClassOf(Type sub, Type sup) {
325N/A return erasure(sup).isAssignableFrom(erasure(sub));
325N/A }
325N/A
325N/A public Class ref(Class c) {
325N/A return c;
325N/A }
325N/A
325N/A public Class use(Class c) {
325N/A return c;
325N/A }
325N/A
325N/A public Class asDecl(Type t) {
325N/A return erasure(t);
325N/A }
325N/A
325N/A public Class asDecl(Class c) {
325N/A return c;
325N/A }
325N/A /**
325N/A * Implements the logic for {@link #erasure(Type)}.
325N/A */
325N/A private static final TypeVisitor<Class, Void> eraser = new TypeVisitor<Class, Void>() {
325N/A
325N/A public Class onClass(Class c, Void _) {
325N/A return c;
325N/A }
325N/A
325N/A public Class onParameterizdType(ParameterizedType p, Void _) {
325N/A // TODO: why getRawType returns Type? not Class?
325N/A return visit(p.getRawType(), null);
325N/A }
325N/A
325N/A public Class onGenericArray(GenericArrayType g, Void _) {
325N/A return Array.newInstance(
325N/A visit(g.getGenericComponentType(), null),
325N/A 0).getClass();
325N/A }
325N/A
325N/A public Class onVariable(TypeVariable v, Void _) {
325N/A return visit(v.getBounds()[0], null);
325N/A }
325N/A
325N/A public Class onWildcard(WildcardType w, Void _) {
325N/A return visit(w.getUpperBounds()[0], null);
325N/A }
325N/A };
325N/A
325N/A /**
325N/A * Returns the runtime representation of the given type.
325N/A *
325N/A * This corresponds to the notion of the erasure in JSR-14.
325N/A *
325N/A * <p>
325N/A * Because of the difference in the way APT and the Java reflection
325N/A * treats primitive type and array type, we can't define this method
325N/A * on {@link Navigator}.
325N/A *
325N/A * <p>
325N/A * It made me realize how difficult it is to define the common navigation
325N/A * layer for two different underlying reflection library. The other way
325N/A * is to throw away the entire parameterization and go to the wrapper approach.
325N/A */
325N/A public <T> Class<T> erasure(Type t) {
325N/A return eraser.visit(t, null);
325N/A }
325N/A
325N/A public boolean isAbstract(Class clazz) {
325N/A return Modifier.isAbstract(clazz.getModifiers());
325N/A }
325N/A
325N/A public boolean isFinal(Class clazz) {
325N/A return Modifier.isFinal(clazz.getModifiers());
325N/A }
325N/A
325N/A /**
325N/A * Returns the {@link Type} object that represents {@code clazz&lt;T1,T2,T3>}.
325N/A */
325N/A public Type createParameterizedType(Class rawType, Type... arguments) {
325N/A return new ParameterizedTypeImpl(rawType, arguments, null);
325N/A }
325N/A
325N/A public boolean isArray(Type t) {
325N/A if (t instanceof Class) {
325N/A Class c = (Class) t;
325N/A return c.isArray();
325N/A }
325N/A if (t instanceof GenericArrayType) {
325N/A return true;
325N/A }
325N/A return false;
325N/A }
325N/A
325N/A public boolean isArrayButNotByteArray(Type t) {
325N/A if (t instanceof Class) {
325N/A Class c = (Class) t;
325N/A return c.isArray() && c != byte[].class;
325N/A }
325N/A if (t instanceof GenericArrayType) {
325N/A t = ((GenericArrayType) t).getGenericComponentType();
325N/A return t != Byte.TYPE;
325N/A }
325N/A return false;
325N/A }
325N/A
325N/A public Type getComponentType(Type t) {
325N/A if (t instanceof Class) {
325N/A Class c = (Class) t;
325N/A return c.getComponentType();
325N/A }
325N/A if (t instanceof GenericArrayType) {
325N/A return ((GenericArrayType) t).getGenericComponentType();
325N/A }
325N/A
325N/A throw new IllegalArgumentException();
325N/A }
325N/A
325N/A public Type getTypeArgument(Type type, int i) {
325N/A if (type instanceof ParameterizedType) {
325N/A ParameterizedType p = (ParameterizedType) type;
325N/A return fix(p.getActualTypeArguments()[i]);
325N/A } else {
325N/A throw new IllegalArgumentException();
325N/A }
325N/A }
325N/A
325N/A public boolean isParameterizedType(Type type) {
325N/A return type instanceof ParameterizedType;
325N/A }
325N/A
325N/A public boolean isPrimitive(Type type) {
325N/A if (type instanceof Class) {
325N/A Class c = (Class) type;
325N/A return c.isPrimitive();
325N/A }
325N/A return false;
325N/A }
325N/A
325N/A public Type getPrimitive(Class primitiveType) {
325N/A assert primitiveType.isPrimitive();
325N/A return primitiveType;
325N/A }
325N/A
325N/A public Location getClassLocation(final Class clazz) {
325N/A return new Location() {
325N/A
325N/A @Override
325N/A public String toString() {
325N/A return clazz.getName();
325N/A }
325N/A };
325N/A }
325N/A
325N/A public Location getFieldLocation(final Field field) {
325N/A return new Location() {
325N/A
325N/A @Override
325N/A public String toString() {
325N/A return field.toString();
325N/A }
325N/A };
325N/A }
325N/A
325N/A public Location getMethodLocation(final Method method) {
325N/A return new Location() {
325N/A
325N/A @Override
325N/A public String toString() {
325N/A return method.toString();
325N/A }
325N/A };
325N/A }
325N/A
325N/A public boolean hasDefaultConstructor(Class c) {
325N/A try {
325N/A c.getDeclaredConstructor();
325N/A return true;
325N/A } catch (NoSuchMethodException e) {
325N/A return false;
325N/A }
325N/A }
325N/A
325N/A public boolean isStaticField(Field field) {
325N/A return Modifier.isStatic(field.getModifiers());
325N/A }
325N/A
325N/A public boolean isPublicMethod(Method method) {
325N/A return Modifier.isPublic(method.getModifiers());
325N/A }
325N/A
325N/A public boolean isPublicField(Field field) {
325N/A return Modifier.isPublic(field.getModifiers());
325N/A }
325N/A
325N/A public boolean isEnum(Class c) {
325N/A return Enum.class.isAssignableFrom(c);
325N/A }
325N/A
325N/A public Field[] getEnumConstants(Class clazz) {
325N/A try {
325N/A Object[] values = clazz.getEnumConstants();
325N/A Field[] fields = new Field[values.length];
325N/A for (int i = 0; i < values.length; i++) {
325N/A fields[i] = clazz.getField(((Enum) values[i]).name());
325N/A }
325N/A return fields;
325N/A } catch (NoSuchFieldException e) {
325N/A // impossible
325N/A throw new NoSuchFieldError(e.getMessage());
325N/A }
325N/A }
325N/A
325N/A public Type getVoidType() {
325N/A return Void.class;
325N/A }
325N/A
325N/A public String getPackageName(Class clazz) {
325N/A String name = clazz.getName();
325N/A int idx = name.lastIndexOf('.');
325N/A if (idx < 0) {
325N/A return "";
325N/A } else {
325N/A return name.substring(0, idx);
325N/A }
325N/A }
325N/A
325N/A public Class findClass(String className, Class referencePoint) {
325N/A try {
325N/A ClassLoader cl = referencePoint.getClassLoader();
325N/A if (cl == null) {
325N/A cl = ClassLoader.getSystemClassLoader();
325N/A }
325N/A return cl.loadClass(className);
325N/A } catch (ClassNotFoundException e) {
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A public boolean isBridgeMethod(Method method) {
325N/A return method.isBridge();
325N/A }
325N/A
325N/A public boolean isOverriding(Method method, Class base) {
325N/A // this isn't actually correct,
325N/A // as the JLS considers
325N/A // class Derived extends Base<Integer> {
325N/A // Integer getX() { ... }
325N/A // }
325N/A // class Base<T> {
325N/A // T getX() { ... }
325N/A // }
325N/A // to be overrided. Handling this correctly needs a careful implementation
325N/A
325N/A String name = method.getName();
325N/A Class[] params = method.getParameterTypes();
325N/A
325N/A while (base != null) {
325N/A try {
325N/A if (base.getDeclaredMethod(name, params) != null) {
325N/A return true;
325N/A }
325N/A } catch (NoSuchMethodException e) {
325N/A // recursively go into the base class
325N/A }
325N/A
325N/A base = base.getSuperclass();
325N/A }
325N/A
325N/A return false;
325N/A }
325N/A
325N/A public boolean isInterface(Class clazz) {
325N/A return clazz.isInterface();
325N/A }
325N/A
325N/A public boolean isTransient(Field f) {
325N/A return Modifier.isTransient(f.getModifiers());
325N/A }
325N/A
325N/A public boolean isInnerClass(Class clazz) {
325N/A return clazz.getEnclosingClass() != null && !Modifier.isStatic(clazz.getModifiers());
325N/A }
325N/A
325N/A /**
325N/A * JDK 5.0 has a bug of creating {@link GenericArrayType} where it shouldn't.
325N/A * fix that manually to work around the problem.
325N/A *
325N/A * See bug 6202725.
325N/A */
325N/A private Type fix(Type t) {
325N/A if (!(t instanceof GenericArrayType)) {
325N/A return t;
325N/A }
325N/A
325N/A GenericArrayType gat = (GenericArrayType) t;
325N/A if (gat.getGenericComponentType() instanceof Class) {
325N/A Class c = (Class) gat.getGenericComponentType();
325N/A return Array.newInstance(c, 0).getClass();
325N/A }
325N/A
325N/A return t;
325N/A }
325N/A}