blob: d25d02c65bd6917b3fefecde6a7ce458b5cb0f78 [file] [log] [blame]
/*
* 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.felix.ipojo.util;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Fluent API to retrieve methods.
*/
public class Methods<T> {
private Class<? extends T> returnType;
private List<Class<?>> argumentTypes = new ArrayList<Class<?>>();
private Class<?> clazz;
private Object object;
private List<Method> methods;
public Map<Method, InvocationResult<T>> map(Object... args) {
Collection<Method> set = retrieve();
Map<Method, InvocationResult<T>> results = new LinkedHashMap<Method, InvocationResult<T>>();
for (Method method : set) {
if (!method.isAccessible()) {
method.setAccessible(true);
}
results.put(method, InvocationResult.<T>fromInvocation(method, object, args));
}
return results;
}
public Collection<InvocationResult<T>> invoke(Object... args) {
return map(args).values();
}
public Methods ofReturnType(Class<? extends T> clazz) {
this.returnType = clazz;
return this;
}
public Methods withParameter(Class<?>... type) {
argumentTypes.addAll(Arrays.asList(type));
return this;
}
public Methods in(Object o) {
this.object = o;
this.clazz = o.getClass();
return this;
}
public Methods in(Class<?> c) {
this.clazz = c;
this.object = null;
return this;
}
private Collection<Method> retrieve() {
if (methods != null) {
return methods;
}
if (clazz == null) {
throw new NullPointerException("Cannot retrieve method, class not set");
}
methods = new ArrayList<Method>();
// First the class itself
Method[] list = clazz.getDeclaredMethods();
for (Method method : list) {
// Two criteria : the return type and the argument type
if (matchReturnType(method)
&& matchArgumentTypes(method)) {
// The method matches
methods.add(method);
}
}
// Traverse class hierarchy
if (clazz.getSuperclass() != null) {
traverse(methods, clazz.getSuperclass());
}
return methods;
}
private boolean matchReturnType(Method method) {
if (returnType == null) { // Void.
return method.getReturnType() == null;
}
return !(method.getReturnType() == null || !returnType.isAssignableFrom(method.getReturnType()));
}
private boolean matchArgumentTypes(Method method) {
// Fast check, the size must be the same.
if (argumentTypes.size() != method.getParameterTypes().length) {
return false;
}
// We have the same size.
for (int i = 0; i < argumentTypes.size(); i++) {
Class<?> argType = method.getParameterTypes()[i];
if (!argumentTypes.get(i).isAssignableFrom(argType)) {
return false;
}
}
return true;
}
private boolean matchInheritanceVisibility(Method method) {
return Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers());
}
private boolean matchNotOverridden(Method method, List<Method> methods) {
for (Method meth : methods) {
if (methodEquality(meth, method)) {
return false;
}
}
return true;
}
/**
* Compares this <code>Method</code> against the specified object. Returns
* true if the objects are the same. Two <code>Methods</code> are the same if
* they were declared by the same class and have the same name
* and formal parameter types and return type.
*/
private boolean methodEquality(Method method1, Method method2) {
if (method1.getName().equals(method2.getName())) {
if (!method1.getReturnType().equals(method2.getReturnType())) {
return false;
}
Class[] params1 = method1.getParameterTypes();
Class[] params2 = method2.getParameterTypes();
if (params1.length == params2.length) {
for (int i = 0; i < params1.length; i++) {
if (params1[i] != params2[i])
return false;
}
return true;
}
}
return false;
}
private void traverse(List<Method> methods, Class<?> clazz) {
// First the given class
Method[] list = clazz.getDeclaredMethods();
for (Method method : list) {
if (matchReturnType(method) && matchArgumentTypes(method) && matchInheritanceVisibility(method)
&& matchNotOverridden(method, methods)) {
methods.add(method);
}
}
// If we have a parent class, traverse it
if (clazz.getSuperclass() != null) {
traverse(methods, clazz.getSuperclass());
}
}
}