/* * Copyright (c) 2010-2013 Evolveum * * 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 com.evolveum.midpoint.util; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import org.apache.commons.lang.StringUtils; /** * @author semancik * */ public class ReflectionUtil { /** * Try to get java property from the object by reflection */ public static <T> T getJavaProperty(Object object, String propertyName, Class<T> propetyClass) { String getterName = getterName(propertyName); Method method; try { method = object.getClass().getMethod(getterName); } catch (SecurityException e) { throw new IllegalArgumentException("Security error getting getter for property "+propertyName+": "+e.getMessage(),e); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("No getter for property "+propertyName+" in "+object+" ("+object.getClass()+")"); } if (method == null) { throw new IllegalArgumentException("No getter for property "+propertyName+" in "+object+" ("+object.getClass()+")"); } if (!propetyClass.isAssignableFrom(method.getReturnType())) { throw new IllegalArgumentException("The getter for property " + propertyName + " returns " + method.getReturnType() + ", expected " + propetyClass+" in "+object+" ("+object.getClass()+")"); } try { return (T) method.invoke(object); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Error invoking getter for property "+propertyName+" in "+object+" ("+object.getClass()+"): " +e.getClass().getSimpleName()+": "+e.getMessage(),e); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Error invoking getter for property "+propertyName+" in "+object+" ("+object.getClass()+"): " +e.getClass().getSimpleName()+": "+e.getMessage(),e); } catch (InvocationTargetException e) { throw new IllegalArgumentException("Error invoking getter for property "+propertyName+" in "+object+" ("+object.getClass()+"): " +e.getClass().getSimpleName()+": "+e.getMessage(),e); } } public static boolean hasJavaProperty(Object object, String propertyName) { return findGetter(object, propertyName) != null; } public static Method findGetter(Object object, String propertyName) { String getterName = getterName(propertyName); return findMethod(object, getterName, 0); } private static String getterName(String propertyName) { return "get" + StringUtils.capitalize(propertyName); } public static Method findMethod(Object object, String methodName, int arity) { for (Method method: object.getClass().getMethods()) { if (method.getName().equals(methodName) && method.getParameterTypes().length == arity && !method.isVarArgs()) { return method; } } return null; } public static Method findMethod(Object object, String methodName, List<?> argList) throws SecurityException { Method method = findMethodDirect(object, methodName, argList); if (method != null) { return method; } method = findMethodCompatible(object, methodName, argList); if (method != null) { return method; } // We cannot find method directly. Try varargs. method = findVarArgsMethod(object, methodName); return method; } private static Method findMethodDirect(Object object, String methodName, List<?> argList) throws SecurityException { Class<?>[] parameterTypes = new Class[argList.size()]; for (int i=0; i < argList.size(); i++) { parameterTypes[i] = argList.get(i).getClass(); } try { return object.getClass().getMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { return null; } } /** * Rough lookup of a compatible method. It takes first method with matching name, number of parameters and compatible * parameter values. It is not perfect, e.g. it cannot select foo(String) instead of foo(Object). But it is better than * nothing. And stock Java reflection has really nothing. */ private static Method findMethodCompatible(Object object, String methodName, List<?> argList) throws SecurityException { for (Method method: object.getClass().getMethods()) { if (method.getName().equals(methodName)) { Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == argList.size()) { boolean wrong = false; for (int i=0; i < parameterTypes.length; i++) { Object arg = argList.get(i); if (arg == null) { // null argument matches any parameter type } else { if (!parameterTypes[i].isAssignableFrom(arg.getClass())) { wrong = true; break; } } } if (!wrong) { // We got it. We have compatible signature here. return method; } } } } return null; } public static Method findVarArgsMethod(Object object, String methodName) { for (Method method: object.getClass().getMethods()) { if (method.getName().equals(methodName) && method.isVarArgs()) { return method; } } return null; } public static Object invokeMethod(Object object, String methodName, List<?> argList) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method method = findMethod(object, methodName, argList); if (method == null) { throw new NoSuchMethodException("No method "+methodName+" for arguments "+debugDumpArgList(argList)+" in "+object); } Object[] args = argList.toArray(); if (method.isVarArgs()) { Class<?> parameterTypeClass = method.getParameterTypes()[0]; Object[] varArgs = (Object[]) Array.newInstance(parameterTypeClass.getComponentType(), args.length); for (int i = 0; i < args.length; i++) { varArgs[i] = args[i]; } args = new Object[] { varArgs }; } try { return method.invoke(object, args); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(e.getMessage()+" for arguments "+debugDumpArgList(argList)+" in "+object, e); } } public static String debugDumpArgList(List<?> argList) { StringBuilder sb = new StringBuilder(); boolean sep = false; for (Object arg: argList) { if (sep) { sb.append(", "); } else { sep = true; } if (arg == null) { sb.append("null"); } else { sb.append(arg.getClass().getName()); sb.append(":"); sb.append(arg); } } return sb.toString(); } }