diff options
Diffstat (limited to 'sca-java-1.x/tags/java-M1-final/java/sca/core/src/main/java/org/apache/tuscany/core/config/JavaIntrospectionHelper.java')
-rw-r--r-- | sca-java-1.x/tags/java-M1-final/java/sca/core/src/main/java/org/apache/tuscany/core/config/JavaIntrospectionHelper.java | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/sca-java-1.x/tags/java-M1-final/java/sca/core/src/main/java/org/apache/tuscany/core/config/JavaIntrospectionHelper.java b/sca-java-1.x/tags/java-M1-final/java/sca/core/src/main/java/org/apache/tuscany/core/config/JavaIntrospectionHelper.java new file mode 100644 index 0000000000..9d74633519 --- /dev/null +++ b/sca-java-1.x/tags/java-M1-final/java/sca/core/src/main/java/org/apache/tuscany/core/config/JavaIntrospectionHelper.java @@ -0,0 +1,446 @@ +/** + * + * Copyright 2005 The Apache Software Foundation or its licensors, as applicable. + * + * Licensed 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.core.config; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Implements various reflection-related operations + * + * @version $Rev$ $Date$ + */ +public class JavaIntrospectionHelper { + + private static final Class[] EMPTY_CLASS_ARRY = new Class[0]; + + /** + * Hide the constructor + */ + private JavaIntrospectionHelper() { + } + + /** + * Returns a collection of public, private, protected, or default fields declared by a class or one of its + * supertypes + */ + public static Set<Field> getAllFields(Class pClass) { + return getAllFields(pClass, new HashSet<Field>()); + } + + /** + * Recursively evaluates the type hierachy to return all fields on a given type + */ + private static Set<Field> getAllFields(Class pClass, Set<Field> fields) { + if (pClass == null || pClass.isArray() || Object.class.equals(pClass)) { + return fields; + } + fields = getAllFields(pClass.getSuperclass(), fields); + Field[] declaredFields = pClass.getDeclaredFields(); + for (Field field : declaredFields) { + field.setAccessible(true); // ignore Java accessibility + fields.add(field); + } + return fields; + } + + /** + * Returns a collection of public, and protected fields declared by a class or one of its + * supertypes + */ + public static Set<Field> getAllPublicAndProtectedFields(Class clazz) { + return getAllPublicAndProtectedFields(clazz, new HashSet<Field>()); + } + + /** + * Recursively evaluates the type hierachy to return all fields that are public or protected + */ + private static Set<Field> getAllPublicAndProtectedFields(Class clazz, Set<Field> fields) { + if (clazz == null || clazz.isArray() || Object.class.equals(clazz)) { + return fields; + } + fields = getAllPublicAndProtectedFields(clazz.getSuperclass(), fields); + Field[] declaredFields = clazz.getDeclaredFields(); +// fields = new HashSet<Field>(); +// Field[] declaredFields = clazz.getFields(); + for (Field field : declaredFields) { + int modifiers = field.getModifiers(); + if ((Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) && !Modifier.isStatic(modifiers)){ + field.setAccessible(true); // ignore Java accessibility + fields.add(field); + } + } + return fields; + } + + /** + * Returns a collection of public, private, protected, or default methods declared by a class or one of + * its supertypes. Note that overriden 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> getAllUniqueMethods(Class clazz) { + return getAllUniqueMethods(clazz, new HashSet<Method>()); + } + + /** + * Recursively evaluates the type hierarchy to return all unique methods + */ + private static Set<Method> getAllUniqueMethods(Class pClass, Set<Method> methods) { + 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 (Method declaredMethod : declaredMethods) { + 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 supclass (i.e. the + // method is not overrided) + if (exactMethodMatch(declaredMethod, method)) { + matched = true; + break; + } + } + if (!matched) { + // TODO ignore Java accessibility + declaredMethod.setAccessible(true); + temp.add(declaredMethod); + + } + methods.addAll(temp); + temp.clear(); + } + } + // evaluate class hierarchy - this is done last to track inherited methods + methods = getAllUniqueMethods(pClass.getSuperclass(), methods); + 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; + } + } + + /** + * Searches a collection of fields for one that matches by name and has a multiplicity type. i.e. a List + * or Array of interfaces + * + * @return a matching field or null + */ + public static Field findMultiplicityFieldByName(String name, Set<Field> fields) { + for (Field candidate : fields) { + if (candidate.getName().equals(name) + && (List.class.isAssignableFrom(candidate.getType()) || (candidate.getType().isArray() + && candidate.getType().getComponentType() != null && candidate.getType().getComponentType() + .isInterface()))) { + return candidate; + } + } + return null; + } + + /** + * Searches a collection of method for one that matches by name and has single parameter of a multiplicity + * type. i.e. a List or Array of interfaces + * + * @return a matching method or null + */ + public static Method findMultiplicityMethodByName(String name, Set<Method> methods) { + for (Method candidate : methods) { + if (candidate.getName().equals(name) + && candidate.getParameterTypes().length == 1 + && (List.class.isAssignableFrom(candidate.getParameterTypes()[0]) || (candidate.getParameterTypes()[0] + .isArray() + && candidate.getParameterTypes()[0].getComponentType() != null && candidate.getParameterTypes()[0] + .getComponentType().isInterface()))) { + return candidate; + } + } + return null; + } + + /** + * Returns a field or method defined in the given class or its superclasses matching a literal name and + * parameter types <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. + * + * @param clazz the class to introspect + * @param propertName the literal name of the property (i.e. JavaBean conventions are not applied) + * @param paramTypes the parameter types for a method or null for fields or methods with no parameters + * @return the field, method or null + */ + public static AccessibleObject getBeanProperty(Class clazz, String propertName, Class[] paramTypes) { + + Set<Method> methods = getAllUniqueMethods(clazz); + for (Method method : methods) { + if (method.getName().equals(propertName)) { + Class[] types = method.getParameterTypes(); + if (types.length == 0 && paramTypes == null) { + return method; + } else if (types.length != 0 && paramTypes == null) { + break; + } else if (types.length == paramTypes.length) { + for (int n = 0; n < types.length - 1; n++) { + if (!types[n].equals(paramTypes[n]) || !types[n].isAssignableFrom(paramTypes[n])) { + break; + } + } + return method; + } + } + } + + Set<Field> fields = getAllFields(clazz); + for (Field field : fields) { + if (field.getName().equals(propertName)) { + return field; + } + } + 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 == 0 && types2.length == 0) { + return true; + } else if (types1.length == types2.length) { + for (int n = 0; n < types1.length; n++) { + if (!types1[n].equals(types2[n])) { + return false; + } + } + return true; + } + return false; + } + + public static <T> Constructor<T> getDefaultConstructor(Class<T> clazz) throws NoSuchMethodException { + return clazz.getConstructor((Class[]) null); + } + + /** + * Loads a class corresponding to the class name using the current context class loader. + * + * @throws ClassNotFoundException if the class was not found on the classpath + */ + public static Class loadClass(String pName) throws ClassNotFoundException { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + return Class.forName(pName, true, loader); + } + + /** + * Returns the simple name of a class - i.e. the class name devoid of its package qualifier + * + * @param implClass + */ + public static String getBaseName(Class<?> implClass) { + String baseName = implClass.getName(); + int lastDot = baseName.lastIndexOf('.'); + if (lastDot != -1) { + baseName = baseName.substring(lastDot + 1); + } + return baseName; + } + + 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) { + return Character.toLowerCase(name.charAt(3)) + name.substring(4); + } + + /** + * 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: + * <p/> + * <code> // to return the generic type of a field: JavaIntrospectionHelper.getGenerics(field.getGenericType()); + * <p/> + * // to return the generic types for the first parameter of a method: JavaIntrospectionHelper.getGenerics(m.getGenericParameterTypes()[0];); + * <p/> + * </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; + } + +} |