/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.webservices.rest.util;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.openmrs.module.webservices.rest.web.annotation.PropertyGetter;
import org.openmrs.module.webservices.rest.web.annotation.PropertySetter;
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingPropertyAccessor;
import org.springframework.util.ReflectionUtils;
/**
* Utility methods for reflection and introspection
*/
public class ReflectionUtil {
private static ConcurrentMap<String, Method> setterMethodCache;
private static ConcurrentMap<String, Method> getterMethodCache;
private static Method nullMethod;
static {
setterMethodCache = new ConcurrentHashMap<String, Method>();
getterMethodCache = new ConcurrentHashMap<String, Method>();
// Just get a method from this class to use as the token null method
nullMethod = ReflectionUtil.class.getDeclaredMethods()[0];
}
public static void clearCaches() {
setterMethodCache = new ConcurrentHashMap<String, Method>();
getterMethodCache = new ConcurrentHashMap<String, Method>();
}
/**
* If clazz implements genericInterface<T, U, ...>, this method returns the parameterized type
* with the given index from that interface. This method will recursively look at superclasses
* until it finds one implementing the requested interface
*
* @should find genericInterface on a superclass if clazz does not directly implement it
* @should ignore type variables on the declaring interface
* @should not inspect superclasses of the specified genericInterface
*/
@SuppressWarnings("rawtypes")
public static Class getParameterizedTypeFromInterface(Class<?> clazz, Class<?> genericInterface, int index) {
for (Type t : clazz.getGenericInterfaces()) {
if (t instanceof ParameterizedType && ((Class) ((ParameterizedType) t).getRawType()).equals(genericInterface)) {
//if we have reached the base interface that declares the type variable T, ignore it
Type pType = ((ParameterizedType) t).getActualTypeArguments()[index];
if (!(pType instanceof TypeVariable)) {
return (Class) pType;
}
}
}
if (clazz.getSuperclass() != null && genericInterface.isAssignableFrom(clazz.getSuperclass())) {
return getParameterizedTypeFromInterface(clazz.getSuperclass(), genericInterface, index);
}
return null;
}
/**
* Find getter method in handler class or any of its superclasses
*
* @param handler
* @param propName
* @return
*/
public static <T> Method findPropertyGetterMethod(DelegatingPropertyAccessor<? extends T> handler, String propName) {
String key = handler.getClass().getName().concat(propName);
Method result = getterMethodCache.get(key);
if (result != null) {
return result == nullMethod ? null : result;
}
Class<?> clazz = handler.getClass();
while (clazz != Object.class && result == null) {
for (Method method : clazz.getMethods()) {
PropertyGetter ann = method.getAnnotation(PropertyGetter.class);
if (ann != null && ann.value().equals(propName)) {
result = method;
break;
}
}
clazz = clazz.getSuperclass();
}
getterMethodCache.put(key, result == null ? nullMethod : result);
return result;
}
/**
* Find setter method in handler class or any of its superclasses
*
* @param handler
* @param propName
* @return
*/
public static <T> Method findPropertySetterMethod(DelegatingPropertyAccessor<? extends T> handler, String propName) {
String key = handler.getClass().getName().concat(propName);
Method result = setterMethodCache.get(key);
if (result != null) {
return result == nullMethod ? null : result;
}
Class<?> clazz = handler.getClass();
while (clazz != Object.class && result == null) {
for (Method method : clazz.getMethods()) {
PropertySetter ann = method.getAnnotation(PropertySetter.class);
if (ann != null && ann.value().equals(propName)) {
result = method;
break;
}
}
clazz = clazz.getSuperclass();
}
setterMethodCache.put(key, result == null ? nullMethod : result);
return result;
}
/**
* @param name the full method name to look for
* @return the java Method object if found. (does not return null)
* @throws RuntimeException if not method found by the given name in the current class
* @param clazz
* @param propName
* @return
*/
public static Method findMethod(Class<?> clazz, String name) {
Method ret = ReflectionUtils.findMethod(clazz, name, (Class<?>[]) null);
if (ret == null)
throw new RuntimeException("No suitable method \"" + name + "\" in " + clazz);
return ret;
}
}