/******************************************************************************* * Copyright (c) 2008 Ralf Ebert * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Ralf Ebert - initial API and implementation *******************************************************************************/ package com.swtxml.util.reflector; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.ObjectUtils; import org.eclipse.core.runtime.Assert; import com.swtxml.util.lang.CollectionUtils; import com.swtxml.util.lang.Filters; import com.swtxml.util.lang.IFilter; public class MethodQuery { private Visibility visibility; private Subclasses subclasses; private List<IFilter<Method>> filters = new ArrayList<IFilter<Method>>(); private static class AnyType { } public static final Class<?> ANY_TYPE = AnyType.class; MethodQuery(Visibility visibility, Subclasses subclasses) { this.visibility = visibility; this.subclasses = subclasses; } private Collection<Method> getMethods(Class<?> type) { if (visibility == Visibility.PUBLIC && subclasses == Subclasses.INCLUDE) { return Arrays.asList(type.getMethods()); } else if (visibility == Visibility.PRIVATE && subclasses == Subclasses.INCLUDE) { return getAllMethods(type); } throw new UnsupportedOperationException("Querying with " + visibility + " and " + subclasses + " not supported at the moment."); }; public MethodQuery nameStartsWith(final String str) { filters.add(new IFilter<Method>() { public boolean match(Method method) { return method.getName().startsWith(str); } @Override public String toString() { return "name starts with \"" + str + "\""; } }); return this; } public MethodQuery nameMatches(final String regex) { filters.add(new IFilter<Method>() { public boolean match(Method method) { return method.getName().matches(regex); } @Override public String toString() { return "name matches \"" + regex + "\""; } }); return this; } public MethodQuery parameters(final Class<?>... signature) { filters.add(new IFilter<Method>() { public boolean match(Method method) { if (method.getParameterTypes().length != signature.length) { return false; } for (int i = 0; i < signature.length; i++) { if (AnyType.class.isAssignableFrom(signature[i])) { continue; } else if (!signature[i].isAssignableFrom(method.getParameterTypes()[i])) { return false; } } return true; } @Override public String toString() { return "parameters=\"" + Arrays.toString(signature) + "\""; } }); return this; } public Collection<Method> all(Class<?> type) { return CollectionUtils.select(getMethods(type), getFilter()); } private IFilter<Method> getFilter() { return Filters.and(filters); } public MethodQuery name(final String name) { filters.add(new IFilter<Method>() { public boolean match(Method method) { return method.getName().equals(name); } @Override public String toString() { return "name=\"" + name + "\""; } }); return this; } public Method exactOne(Class<?> type) { Collection<Method> results = all(type); if (results.size() == 1) { return results.iterator().next(); } else if (results.isEmpty()) { throw new ReflectorException("No method " + getFilter() + " found in " + type + "!"); } else { throw new ReflectorException("Ambiguous methods found for " + getFilter() + " in " + type + ": " + results); } } public MethodQuery optionalParameter(final Class<?> type) { Assert.isNotNull(type, "type"); filters.add(new IFilter<Method>() { public boolean match(Method method) { return method.getParameterTypes().length == 0 || (method.getParameterTypes().length == 1 && type == method .getParameterTypes()[0]); } @Override public String toString() { return "optional parameter \"" + type.getSimpleName() + "\""; } }); return this; } public MethodQuery returnType(final Class<?> type) { filters.add(new IFilter<Method>() { public boolean match(Method method) { return ObjectUtils.equals(type, method.getReturnType()); } @Override public String toString() { return "return type \"" + (type != null ? type.getSimpleName() : "null") + "\""; } }); return this; } /** * Same as Class.getMethods() but with private methods included. Returns all * methods of <type> and its superclasses, overwritten superclass methods * are not included. */ private Collection<Method> getAllMethods(Class<?> type) { Map<String, Method> signatureToMethod = new HashMap<String, Method>(); while (type != null) { for (Method method : type.getDeclaredMethods()) { if (method.isBridge() || method.isSynthetic()) { continue; } String signature = getSignature(method); if (!signatureToMethod.containsKey(signature)) { signatureToMethod.put(signature, method); } } type = type.getSuperclass(); } return signatureToMethod.values(); } private String getSignature(Method method) { StringBuffer s = new StringBuffer(); s.append(method.getName()); for (Class<?> param : method.getParameterTypes()) { s.append(':'); s.append(param.getName()); } return s.toString(); } }