| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.tuscany.sca.implementation.java.introspect; |
| |
| import java.beans.Introspector; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.AnnotatedElement; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.lang.reflect.TypeVariable; |
| import java.lang.reflect.WildcardType; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.logging.Logger; |
| |
| import org.oasisopen.sca.ServiceReference; |
| |
| /** |
| * Implements various reflection-related operations |
| * |
| * @version $Rev$ $Date$ |
| */ |
| public final class JavaIntrospectionHelper { |
| private static final Logger logger = Logger.getLogger(JavaIntrospectionHelper.class.getName()); |
| private static final Class<?>[] EMPTY_CLASS_ARRY = new Class[0]; |
| |
| /** |
| * Hide the constructor |
| */ |
| private JavaIntrospectionHelper() { |
| } |
| |
| /** |
| * Returns a collection of public, and protected fields declared by a class |
| * or one of its supertypes |
| */ |
| public static Set<Field> getAllPublicAndProtectedFields(Class<?> clazz, boolean validating) { |
| return getAllPublicAndProtectedFields(clazz, new HashSet<Field>(), validating); |
| } |
| |
| private static void checkInvalidAnnotations(AnnotatedElement element) { |
| for (Annotation a : element.getAnnotations()) { |
| if (a.annotationType().getName().startsWith("org.oasisopen.sca.annotation.")) { |
| logger.warning("Invalid annotation " + a + " is found on " + element); |
| } |
| } |
| } |
| |
| /** |
| * Recursively evaluates the type hierarchy to return all fields that are |
| * public or protected |
| */ |
| private static Set<Field> getAllPublicAndProtectedFields(Class<?> clazz, Set<Field> fields, boolean validating) { |
| if (clazz == null || clazz.isArray() || Object.class.equals(clazz)) { |
| return fields; |
| } |
| fields = getAllPublicAndProtectedFields(clazz.getSuperclass(), fields, validating); |
| Field[] declaredFields = clazz.getDeclaredFields(); |
| for (final Field field : declaredFields) { |
| int modifiers = field.getModifiers(); |
| // The field should be non-final and non-static |
| if ((Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) && !Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) { |
| // Allow privileged access to set accessibility. Requires ReflectPermission |
| // in security policy. |
| AccessController.doPrivileged(new PrivilegedAction<Object>() { |
| public Object run() { |
| field.setAccessible(true); // ignore Java accessibility |
| return null; |
| } |
| }); |
| fields.add(field); |
| } /*else { |
| if (validating) { |
| checkInvalidAnnotations(field); |
| } |
| }*/ |
| } |
| return fields; |
| } |
| |
| /** |
| * Returns a collection of injectable fields declared by a class |
| * or one of its supertypes |
| * |
| * For now we will include final or static fields so that validation problems can be reported |
| */ |
| public static Set<Field> getInjectableFields(Class<?> clazz, boolean validating) { |
| return getInjectableFields(clazz, new HashSet<Field>(), validating); |
| } |
| |
| /** |
| * Recursively evaluates the type hierarchy to return all fields |
| */ |
| private static Set<Field> getInjectableFields(Class<?> clazz, Set<Field> fields, boolean validating) { |
| if (clazz == null || clazz.isArray() || Object.class.equals(clazz)) { |
| return fields; |
| } |
| fields = getInjectableFields(clazz.getSuperclass(), fields, validating); |
| Field[] declaredFields = clazz.getDeclaredFields(); |
| for (final Field field : declaredFields) { |
| int modifiers = field.getModifiers(); |
| // The field should be non-final and non-static |
| if (!Modifier.isStatic(modifiers) |
| // && !Modifier.isFinal(modifiers) |
| ) { |
| |
| // Allow privileged access to set accessibility. Requires ReflectPermission |
| // in security policy. |
| AccessController.doPrivileged(new PrivilegedAction<Object>() { |
| public Object run() { |
| field.setAccessible(true); // ignore Java accessibility |
| return null; |
| } |
| }); |
| fields.add(field); |
| } else { |
| if (validating) { |
| checkInvalidAnnotations(field); |
| } |
| } |
| } |
| return fields; |
| } |
| |
| /** |
| * Returns a collection of public and protected methods declared by a class |
| * or one of its supertypes. Note that overridden methods will not be |
| * returned in the collection (i.e. only the method override will be). <p/> |
| * This method can potentially be expensive as reflection information is not |
| * cached. It is assumed that this method will be used during a |
| * configuration phase. |
| */ |
| public static Set<Method> getAllUniquePublicProtectedMethods(Class<?> clazz, boolean validating) { |
| return getAllUniqueMethods(clazz, new HashSet<Method>(), validating); |
| } |
| |
| /** |
| * Recursively evaluates the type hierarchy to return all unique methods |
| */ |
| private static Set<Method> getAllUniqueMethods(Class<?> pClass, Set<Method> methods, boolean validating) { |
| if (pClass == null || pClass.isArray() || Object.class.equals(pClass)) { |
| return methods; |
| } |
| // we first evaluate methods of the subclass and then move to the parent |
| Method[] declaredMethods = pClass.getDeclaredMethods(); |
| for (final Method declaredMethod : declaredMethods) { |
| int modifiers = declaredMethod.getModifiers(); |
| if ((!Modifier.isPublic(modifiers) && !Modifier.isProtected(modifiers)) || Modifier.isStatic(modifiers)) { |
| if (validating) { |
| checkInvalidAnnotations(declaredMethod); |
| } |
| continue; |
| } |
| if (methods.size() == 0) { |
| methods.add(declaredMethod); |
| } else { |
| List<Method> temp = new ArrayList<Method>(); |
| boolean matched = false; |
| for (Method method : methods) { |
| // only add if not already in the set from a superclass (i.e. |
| // the method is not overridden) |
| if (exactMethodMatch(declaredMethod, method)) { |
| matched = true; |
| break; |
| } |
| } |
| if (!matched) { |
| // Allow privileged access to set accessibility. Requires ReflectPermission |
| // in security policy. |
| AccessController.doPrivileged(new PrivilegedAction<Object>() { |
| public Object run() { |
| declaredMethod.setAccessible(true); |
| return null; |
| } |
| }); |
| temp.add(declaredMethod); |
| } |
| methods.addAll(temp); |
| temp.clear(); |
| } |
| } |
| // evaluate class hierarchy - this is done last to track inherited |
| // methods |
| methods = getAllUniqueMethods(pClass.getSuperclass(), methods, validating); |
| return methods; |
| } |
| |
| /** |
| * Finds the closest matching field with the given name, that is, a field of |
| * the exact specified type or, alternately, of a supertype. |
| * |
| * @param name the name of the field |
| * @param type the field type |
| * @param fields the collection of fields to search |
| * @return the matching field or null if not found |
| */ |
| public static Field findClosestMatchingField(String name, Class type, Set<Field> fields) { |
| Field candidate = null; |
| for (Field field : fields) { |
| if (field.getName().equals(name)) { |
| if (field.getType().equals(type)) { |
| return field; // exact match |
| } else if (field.getType().isAssignableFrom(type) || (field.getType().isPrimitive() && primitiveAssignable(field |
| .getType(), |
| type))) { |
| // We could have the situation where a field parameter is a |
| // primitive and the demarshalled value is |
| // an object counterpart (e.g. Integer and int) |
| // @spec issue |
| // either an interface or super class, so keep a reference |
| // until |
| // we know there are no closer types |
| candidate = field; |
| } |
| } |
| } |
| if (candidate != null) { |
| return candidate; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Finds the closest matching method with the given name, that is, a method |
| * taking the exact parameter types or, alternately, parameter supertypes. |
| * |
| * @param name the name of the method |
| * @param types the method parameter types |
| * @param methods the collection of methods to search |
| * @return the matching method or null if not found |
| */ |
| public static Method findClosestMatchingMethod(String name, Class[] types, Set<Method> methods) { |
| if (types == null) { |
| types = EMPTY_CLASS_ARRY; |
| } |
| Method candidate = null; |
| for (Method method : methods) { |
| if (method.getName().equals(name) && method.getParameterTypes().length == types.length) { |
| Class<?>[] params = method.getParameterTypes(); |
| boolean disqualify = false; |
| boolean exactMatch = true; |
| for (int i = 0; i < params.length; i++) { |
| if (!params[i].equals(types[i]) && !params[i].isAssignableFrom(types[i])) { |
| // no match |
| disqualify = true; |
| exactMatch = false; |
| break; |
| } else if (!params[i].equals(types[i]) && params[i].isAssignableFrom(types[i])) { |
| // not exact match |
| exactMatch = false; |
| } |
| } |
| if (disqualify) { |
| continue; |
| } else if (exactMatch) { |
| return method; |
| } else { |
| candidate = method; |
| } |
| } |
| } |
| if (candidate != null) { |
| return candidate; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Determines if two methods "match" - that is, they have the same method |
| * names and exact parameter types (one is not a supertype of the other) |
| */ |
| public static boolean exactMethodMatch(Method method1, Method method2) { |
| if (!method1.getName().equals(method2.getName())) { |
| return false; |
| } |
| Class<?>[] types1 = method1.getParameterTypes(); |
| Class<?>[] types2 = method2.getParameterTypes(); |
| if (types1.length != types2.length) { |
| return false; |
| } |
| boolean matched = true; |
| for (int i = 0; i < types1.length; i++) { |
| if (types1[i] != types2[i]) { |
| matched = false; |
| break; |
| } |
| } |
| return matched; |
| } |
| |
| public static <T> Constructor<T> getDefaultConstructor(Class<T> clazz) throws NoSuchMethodException { |
| return clazz.getConstructor((Class[])null); |
| } |
| |
| /** |
| * Returns the simple name of a class - i.e. the class name devoid of its |
| * package qualifier |
| * |
| * @param implClass the implementation class |
| */ |
| public static String getBaseName(Class<?> implClass) { |
| return implClass.getSimpleName(); |
| } |
| |
| public static boolean isImmutable(Class<?> clazz) { |
| return String.class == clazz || clazz.isPrimitive() |
| || Number.class.isAssignableFrom(clazz) |
| || Boolean.class.isAssignableFrom(clazz) |
| || Character.class.isAssignableFrom(clazz) |
| || Byte.class.isAssignableFrom(clazz); |
| } |
| |
| /** |
| * Takes a property name and converts it to a getter method name according |
| * to JavaBean conventions. For example, property |
| * <code>foo<code> is returned as <code>getFoo</code> |
| */ |
| public static String toGetter(String name) { |
| return "get" + name.toUpperCase().substring(0, 1) + name.substring(1); |
| } |
| |
| /** |
| * Takes a setter or getter method name and converts it to a property name |
| * according to JavaBean conventions. For example, <code>setFoo(var)</code> |
| * is returned as property <code>foo<code> |
| */ |
| public static String toPropertyName(String name) { |
| if (!name.startsWith("set")) { |
| return name; |
| } |
| return Introspector.decapitalize(name.substring(3)); |
| } |
| |
| public static Class<?> getErasure(Type type) { |
| if (type instanceof Class) { |
| return (Class<?>)type; |
| } else if (type instanceof GenericArrayType) { |
| // FIXME: How to deal with the []? |
| GenericArrayType arrayType = (GenericArrayType)type; |
| return getErasure(arrayType.getGenericComponentType()); |
| } else if (type instanceof ParameterizedType) { |
| ParameterizedType pType = (ParameterizedType)type; |
| return getErasure(pType.getRawType()); |
| } else if (type instanceof WildcardType) { |
| WildcardType wType = (WildcardType)type; |
| Type[] types = wType.getUpperBounds(); |
| return getErasure(types[0]); |
| } else if (type instanceof TypeVariable) { |
| TypeVariable var = (TypeVariable)type; |
| Type[] types = var.getBounds(); |
| return getErasure(types[0]); |
| } |
| return null; |
| } |
| |
| public static Class<?> getBaseType(Class<?> cls, Type genericType) { |
| if (cls.isArray()) { |
| return cls.getComponentType(); |
| } else if (Collection.class.isAssignableFrom(cls)) { |
| if (genericType instanceof ParameterizedType) { |
| // Collection<BaseType> |
| ParameterizedType parameterizedType = (ParameterizedType)genericType; |
| Type baseType = parameterizedType.getActualTypeArguments()[0]; |
| if (baseType instanceof GenericArrayType) { |
| // Base is array |
| return cls; |
| } else { |
| return getErasure(baseType); |
| } |
| } else { |
| return cls; |
| } |
| } else { |
| return cls; |
| } |
| } |
| |
| public static Type getParameterType(Type type) { |
| if (type instanceof ParameterizedType) { |
| // Collection<BaseType> |
| ParameterizedType parameterizedType = (ParameterizedType)type; |
| Type baseType = parameterizedType.getActualTypeArguments()[0]; |
| return baseType; |
| } else { |
| return Object.class; |
| } |
| } |
| |
| public static Class<?> getBusinessInterface(Class<?> cls, Type callableReferenceType) { |
| if (ServiceReference.class.isAssignableFrom(cls) && callableReferenceType instanceof ParameterizedType) { |
| // Collection<BaseType> |
| ParameterizedType parameterizedType = (ParameterizedType)callableReferenceType; |
| Type baseType = parameterizedType.getActualTypeArguments()[0]; |
| if (baseType instanceof GenericArrayType) { |
| // Base is array |
| return cls; |
| } else { |
| return getErasure(baseType); |
| } |
| } |
| return Object.class; |
| } |
| |
| /** |
| * Takes a property name and converts it to a setter method name according |
| * to JavaBean conventions. For example, the property |
| * <code>foo<code> is returned as <code>setFoo(var)</code> |
| */ |
| public static String toSetter(String name) { |
| return "set" + name.toUpperCase().substring(0, 1) + name.substring(1); |
| } |
| |
| /** |
| * Compares a two types, assuming one is a primitive, to determine if the |
| * other is its object counterpart |
| */ |
| private static boolean primitiveAssignable(Class<?> memberType, Class<?> param) { |
| if (memberType == Integer.class) { |
| return param == Integer.TYPE; |
| } else if (memberType == Double.class) { |
| return param == Double.TYPE; |
| } else if (memberType == Float.class) { |
| return param == Float.TYPE; |
| } else if (memberType == Short.class) { |
| return param == Short.TYPE; |
| } else if (memberType == Character.class) { |
| return param == Character.TYPE; |
| } else if (memberType == Boolean.class) { |
| return param == Boolean.TYPE; |
| } else if (memberType == Byte.class) { |
| return param == Byte.TYPE; |
| } else if (param == Integer.class) { |
| return memberType == Integer.TYPE; |
| } else if (param == Double.class) { |
| return memberType == Double.TYPE; |
| } else if (param == Float.class) { |
| return memberType == Float.TYPE; |
| } else if (param == Short.class) { |
| return memberType == Short.TYPE; |
| } else if (param == Character.class) { |
| return memberType == Character.TYPE; |
| } else if (param == Boolean.class) { |
| return memberType == Boolean.TYPE; |
| } else if (param == Byte.class) { |
| return memberType == Byte.TYPE; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Returns the generic types represented in the given type. Usage as |
| * follows: <code> |
| * JavaIntrospectionHelper.getGenerics(field.getGenericType()); |
| * <p/> |
| * JavaIntrospectionHelper.getGenerics(m.getGenericParameterTypes()[0];); </code> |
| * |
| * @return the generic types in order of declaration or an empty array if |
| * the type is not genericized |
| */ |
| public static List<? extends Type> getGenerics(Type genericType) { |
| List<Type> classes = new ArrayList<Type>(); |
| if (genericType instanceof ParameterizedType) { |
| ParameterizedType ptype = (ParameterizedType)genericType; |
| // get the type arguments |
| Type[] targs = ptype.getActualTypeArguments(); |
| for (Type targ : targs) { |
| classes.add(targ); |
| } |
| } |
| return classes; |
| } |
| |
| /** |
| * Returns the generic type specified by the class at the given position as |
| * in: <p/> <code> public class Foo<Bar,Baz>{ //.. } |
| * <p/> |
| * JavaIntrospectionHelper.introspectGeneric(Foo.class,1); <code> |
| * <p/> |
| * will return Baz. |
| */ |
| public static Class introspectGeneric(Class<?> clazz, int pos) { |
| assert clazz != null : "No class specified"; |
| Type type = clazz.getGenericSuperclass(); |
| if (type instanceof ParameterizedType) { |
| Type[] args = ((ParameterizedType)type).getActualTypeArguments(); |
| if (args.length <= pos) { |
| throw new IllegalArgumentException("Invalid index value for generic class " + clazz.getName()); |
| } |
| return (Class)((ParameterizedType)type).getActualTypeArguments()[pos]; |
| } else { |
| Type[] interfaces = clazz.getGenericInterfaces(); |
| for (Type itype : interfaces) { |
| if (!(itype instanceof ParameterizedType)) { |
| continue; |
| } |
| ParameterizedType interfaceType = (ParameterizedType)itype; |
| return (Class)interfaceType.getActualTypeArguments()[0]; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the set of interfaces implemented by the given class and its |
| * ancestors or a blank set if none |
| */ |
| public static Set<Class<?>> getAllInterfaces(Class<?> clazz) { |
| Set<Class<?>> implemented = new HashSet<Class<?>>(); |
| getAllInterfaces(clazz, implemented); |
| return implemented; |
| } |
| |
| private static void getAllInterfaces(Class<?> clazz, Set<Class<?>> implemented) { |
| Class<?>[] interfaces = clazz.getInterfaces(); |
| for (Class<?> interfaze : interfaces) { |
| implemented.add(interfaze); |
| } |
| Class<?> superClass = clazz.getSuperclass(); |
| // Object has no superclass so check for null |
| if (superClass != null && !superClass.equals(Object.class)) { |
| getAllInterfaces(superClass, implemented); |
| } |
| } |
| |
| public static boolean isSetter(Method method) { |
| return (void.class == method.getReturnType() && method.getParameterTypes().length == 1 && method.getName() |
| .startsWith("set")); |
| } |
| |
| public static boolean isGetter(Method method) { |
| return (void.class != method.getReturnType() && method.getParameterTypes().length == 0 && method.getName() |
| .startsWith("get")); |
| } |
| |
| private final static Map<Class<?>, String> signatures = new HashMap<Class<?>, String>(); |
| static { |
| signatures.put(boolean.class, "Z"); |
| signatures.put(byte.class, "B"); |
| signatures.put(char.class, "C"); |
| signatures.put(short.class, "S"); |
| signatures.put(int.class, "I"); |
| signatures.put(long.class, "J"); |
| signatures.put(float.class, "F"); |
| signatures.put(double.class, "D"); |
| }; |
| |
| public static String getSignature(Class<?> cls) { |
| if (cls.isPrimitive()) { |
| return signatures.get(cls); |
| } |
| if (cls.isArray()) { |
| return "[" + getSignature(cls.getComponentType()); |
| } |
| return "L" + cls.getName().replace('.', '/') + ";"; |
| } |
| |
| public static Class<?> getArrayType(Class<?> componentType, int dims) throws ClassNotFoundException { |
| StringBuffer buf = new StringBuffer(); |
| for (int i = 0; i < dims; i++) { |
| buf.append('['); |
| } |
| buf.append(getSignature(componentType)); |
| return Class.forName(buf.toString(), false, componentType.getClassLoader()); |
| } |
| |
| public static Set<Method> getPrivateMethods(Class<?> clazz) { |
| Set<Method> methods = new HashSet<Method>(); |
| Method[] declaredMethods = clazz.getDeclaredMethods(); |
| for (final Method declaredMethod : declaredMethods) { |
| int modifiers = declaredMethod.getModifiers(); |
| if(Modifier.isPrivate(modifiers)) { |
| methods.add(declaredMethod); |
| } |
| } |
| |
| return methods; |
| } |
| |
| public static Set<Field> getPrivateFields(Class<?> clazz) { |
| Set<Field> fields = new HashSet<Field>(); |
| Field[] declaredFields = clazz.getDeclaredFields(); |
| for (final Field declaredField : declaredFields) { |
| int modifiers = declaredField.getModifiers(); |
| if(Modifier.isPrivate(modifiers)) { |
| fields.add(declaredField); |
| } |
| } |
| |
| return fields; |
| } |
| } |