/* * 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()); } } }