All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.jsonrpc4j.ReflectionUtil Maven / Gradle / Ivy

package com.googlecode.jsonrpc4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Utilities for reflection.
 */
public abstract class ReflectionUtil {
	
	private static final Map> methodCache = new ConcurrentHashMap<>();
	
	private static final Map>> parameterTypeCache = new ConcurrentHashMap<>();
	
	private static final Map> methodAnnotationCache = new ConcurrentHashMap<>();
	
	private static final Map>> methodParamAnnotationCache = new ConcurrentHashMap<>();
	
	/**
	 * Finds methods with the given name on the given class.
	 *
	 * @param classes                    the classes
	 * @param name                       the method name
	 * @return the methods
	 */
	static Set findCandidateMethods(Class[] classes, String name) {
		StringBuilder sb = new StringBuilder();
		for (Class clazz : classes) {
			sb.append(clazz.getName()).append("::");
		}
		String cacheKey = sb.append(name).toString();
		if (methodCache.containsKey(cacheKey)) {
			return methodCache.get(cacheKey);
		}
		Set methods = new HashSet<>();
		for (Class clazz : classes) {
            for (Method method : clazz.getMethods()) {
                if (method.isAnnotationPresent(JsonRpcMethod.class)) {
                    JsonRpcMethod methodAnnotation = method.getAnnotation(JsonRpcMethod.class);

                    if (methodAnnotation.required()) {
                        if (methodAnnotation.value().equals(name)) {
                            methods.add(method);
                        }
                    } else if (methodAnnotation.value().equals(name) || method.getName().equals(name)) {
                        methods.add(method);
                    }
                } else if (method.getName().equals(name)) {
                    methods.add(method);
                }
            }
        }
		methods = Collections.unmodifiableSet(methods);
		methodCache.put(cacheKey, methods);
		return methods;
	}
	
	/**
	 * Returns the parameter types for the given {@link Method}.
	 *
	 * @param method the {@link Method}
	 * @return the parameter types
	 */
	static List> getParameterTypes(Method method) {
		if (parameterTypeCache.containsKey(method)) {
			return parameterTypeCache.get(method);
		}
		List> types = new ArrayList<>();
		Collections.addAll(types, method.getParameterTypes());
		types = Collections.unmodifiableList(types);
		parameterTypeCache.put(method, types);
		return types;
	}
	
	/**
	 * Returns {@link Annotation}s of the given type defined
	 * on the given {@link Method}.
	 *
	 * @param     the {@link Annotation} type
	 * @param method the {@link Method}
	 * @param type   the type
	 * @return the {@link Annotation}s
	 */
	public static  List getAnnotations(Method method, Class type) {
		return filterAnnotations(getAnnotations(method), type);
	}
	
	private static  List filterAnnotations(Collection annotations, Class type) {
		List result = new ArrayList<>();
		for (Annotation annotation : annotations) {
			if (type.isInstance(annotation)) {
				result.add(type.cast(annotation));
			}
		}
		return result;
	}
	
	/**
	 * Returns all of the {@link Annotation}s defined on
	 * the given {@link Method}.
	 *
	 * @param method the {@link Method}
	 * @return the {@link Annotation}s
	 */
	private static List getAnnotations(Method method) {
		if (methodAnnotationCache.containsKey(method)) {
			return methodAnnotationCache.get(method);
		}
		List annotations = new ArrayList<>();
		Collections.addAll(annotations, method.getAnnotations());
		annotations = Collections.unmodifiableList(annotations);
		methodAnnotationCache.put(method, annotations);
		return annotations;
	}
	
	/**
	 * Returns the first {@link Annotation} of the given type
	 * defined on the given {@link Method}.
	 *
	 * @param     the type
	 * @param method the method
	 * @param type   the type of annotation
	 * @return the annotation or null
	 */
	public static  T getAnnotation(Method method, Class type) {
		for (Annotation a : getAnnotations(method)) {
			if (type.isInstance(a)) {
				return type.cast(a);
			}
		}
		return null;
	}
	
	/**
	 * Returns the parameter {@link Annotation}s of the
	 * given type for the given {@link Method}.
	 *
	 * @param     the {@link Annotation} type
	 * @param type   the type
	 * @param method the {@link Method}
	 * @return the {@link Annotation}s
	 */
	static  List> getParameterAnnotations(Method method, Class type) {
		List> annotations = new ArrayList<>();
		for (List paramAnnotations : getParameterAnnotations(method)) {
			annotations.add(filterAnnotations(paramAnnotations, type));
		}
		return annotations;
	}
	
	/**
	 * Returns the parameter {@link Annotation}s for the
	 * given {@link Method}.
	 *
	 * @param method the {@link Method}
	 * @return the {@link Annotation}s
	 */
	private static List> getParameterAnnotations(Method method) {
		if (methodParamAnnotationCache.containsKey(method)) {
			return methodParamAnnotationCache.get(method);
		}
		List> annotations = new ArrayList<>();
		for (Annotation[] paramAnnotations : method.getParameterAnnotations()) {
			List listAnnotations = new ArrayList<>();
			Collections.addAll(listAnnotations, paramAnnotations);
			annotations.add(listAnnotations);
		}
		annotations = Collections.unmodifiableList(annotations);
		methodParamAnnotationCache.put(method, annotations);
		return annotations;
	}
	
	/**
	 * Parses the given arguments for the given method optionally
	 * turning them into named parameters.
	 *
	 * @param method    the method
	 * @param arguments the arguments
	 * @return the parsed arguments
	 */
	public static Object parseArguments(Method method, Object[] arguments) {

		JsonRpcParamsPassMode paramsPassMode = JsonRpcParamsPassMode.AUTO;
		JsonRpcMethod jsonRpcMethod = getAnnotation(method, JsonRpcMethod.class);
		if (jsonRpcMethod != null)
			paramsPassMode = jsonRpcMethod.paramsPassMode();

		Map namedParams = getNamedParameters(method, arguments);

		switch (paramsPassMode) {
			case ARRAY:
				if (namedParams.size() > 0) {
					Object[] parsed = new Object[namedParams.size()];
					int i = 0;
					for (Object value : namedParams.values()) {
						parsed[i++] = value;
					}
					return parsed;
				} else {
					return arguments != null ? arguments : new Object[]{};
				}
			case OBJECT:
				if (namedParams.size() > 0) {
					return namedParams;
				} else {
					if (arguments == null) {
                        return new Object[]{};
                    }
					throw new IllegalArgumentException(
							"OBJECT parameters pass mode is impossible without declaring JsonRpcParam annotations for all parameters on method "
									+ method.getName());
				}
			case AUTO:
			default:
				if (namedParams.size() > 0) {
					return namedParams;
				} else {
					return arguments != null ? arguments : new Object[]{};
				}
		}
	}
	
	/**
	 * Checks method for @JsonRpcParam annotations and returns named parameters.
	 *
	 * @param method    the method
	 * @param arguments the arguments
	 * @return named parameters or empty if no annotations found
	 * @throws IllegalArgumentException if some parameters are annotated and others not
	 */
	private static Map getNamedParameters(Method method, Object[] arguments) {
		
		Map namedParams = new LinkedHashMap<>();
		
		Annotation[][] paramAnnotations = method.getParameterAnnotations();
		for (int i = 0; i < paramAnnotations.length; i++) {
			Annotation[] ann = paramAnnotations[i];
			for (Annotation an : ann) {
				if (JsonRpcParam.class.isInstance(an)) {
					JsonRpcParam jAnn = (JsonRpcParam) an;
					namedParams.put(jAnn.value(), arguments[i]);
					break;
				}
			}
		}
		
		if (arguments != null && arguments.length > 0 && namedParams.size() > 0 && namedParams.size() != arguments.length) {
			throw new IllegalArgumentException("JsonRpcParam annotations were not found for all parameters on method " + method.getName());
		}
		
		return namedParams;
	}

	public static void clearCache() {
		methodCache.clear();
		parameterTypeCache.clear();
		methodAnnotationCache.clear();
		methodParamAnnotationCache.clear();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy