/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2000 Blacksmith, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org */ package net.wotonomy.foundation; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Comparator; import net.wotonomy.foundation.internal.PropertyComparator; /** * A pure java implementation of NSSelector. * * @author michael@mpowers.net * @author $Author: cgruber $ * @version $Revision: 892 $ */ public class NSSelector implements Comparator, Serializable { private static final long serialVersionUID = 2531682514697198972L; protected NSMutableDictionary methodMap; // map of classes to methods protected String methodName; protected Class[] parameterTypes; /** * A marker to indicate object not found. */ protected static final String NOT_FOUND = "NOT_FOUND"; /** * Saves creating a new class array for parameterless method invocation. */ protected static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; /** * Saves creating a new object array for parameterless method invocation. */ protected static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; /** * Constructor specifying a method name and an array of parameter types. */ public NSSelector(String aMethodName, Class[] aParameterTypeArray) { methodName = aMethodName; parameterTypes = aParameterTypeArray; methodMap = new NSMutableDictionary(); } /** * Constructor specifying a method name with no parameters. */ public NSSelector(String aMethodName) { this(aMethodName, EMPTY_CLASS_ARRAY); } /** * Constructor for custom subclasses that implement specific operators and that * do not use dynamic method invocation. */ protected NSSelector() { } /** * Returns the name of the method. */ public String name() { return methodName; } /** * Returns the array of parameter types. */ public Class[] parameterTypes() { return parameterTypes; } /** * A String description of this selector. */ public String toString() { StringBuffer result = new StringBuffer(); result.append("[" + getClass().getName() + ": name = " + name() + ", parameter types = ["); if (parameterTypes != null) { if (parameterTypes.length > 0) { result.append(parameterTypes[0].toString()); } for (int i = 1; i < parameterTypes.length; i++) { result.append(", "); result.append(parameterTypes[i].toString()); } } result.append("] ]"); return result.toString(); } /** * Returns the appropriate method for the specified class. */ public Method methodOnClass(Class aClass) throws NoSuchMethodException { Object result = methodMap.objectForKey(aClass); if (result == null) { result = getMethodForClass(aClass); if (result == null) { result = NOT_FOUND; } methodMap.setObjectForKey(result, aClass); } if (result == NOT_FOUND) { throw new NoSuchMethodException(); } return (Method) result; } /** * Returns the appropriate method, or null if not found. */ private Method getMethodForClass(Class aClass) { Method[] methods = aClass.getMethods(); for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(name())) { Class[] params = methods[i].getParameterTypes(); if (params.length == parameterTypes.length) { boolean pass = true; for (int j = 0; j < params.length; j++) { if (!params[j].isAssignableFrom(parameterTypes[j])) { pass = false; } } if (pass) return methods[i]; } } } return null; } /** * Convenience to get a method for an object. */ public Method methodOnObject(Object anObject) throws NoSuchMethodException { Method m = methodOnClass(anObject.getClass()); if (m == null) throw new NoSuchMethodException(name()); return m; } /** * Returns whether the class implements the method for this selector. */ public boolean implementedByClass(Class aClass) { try { methodOnClass(aClass); return true; } catch (NoSuchMethodException exc) { } return false; } /** * Returns whether the object's class implements the method for this selector. */ public boolean implementedByObject(Object anObject) { try { methodOnObject(anObject); return true; } catch (NoSuchMethodException exc) { } return false; } /** * Invokes this selector's method on the specified object using the specified * parameters. */ public Object invoke(Object anObject, Object[] parameters) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { return methodOnObject(anObject).invoke(anObject, parameters); } /** * Invokes this selector's method on the specified object with no parameters. */ public Object invoke(Object anObject) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { return invoke(anObject, EMPTY_OBJECT_ARRAY); } /** * Invokes this selector's method on the specified object with the specified * parameter. */ public Object invoke(Object anObject, Object aParameter) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { return invoke(anObject, new Object[] { aParameter }); } /** * Invokes this selector's method on the specified object using the specified * two parameters. */ public Object invoke(Object anObject, Object p1, Object p2) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { return invoke(anObject, new Object[] { p1, p2 }); } /** * Invokes the method with the specified signature on the specified object using * the specified parameters. */ public static Object invoke(String methodName, Class[] parameterTypes, Object anObject, Object[] parameters) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { return new NSSelector(methodName, parameterTypes).invoke(anObject, parameters); } /** * Invokes the method with the specified signature on the specified object with * no parameters. */ public static Object invoke(String methodName, Object anObject) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { return NSSelector.invoke(methodName, EMPTY_CLASS_ARRAY, anObject, EMPTY_OBJECT_ARRAY); } /** * Invokes the method with the specified signature on the specified object using * the specified parameter. */ public static Object invoke(String methodName, Class[] parameterTypes, Object anObject, Object aParameter) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { return NSSelector.invoke(methodName, parameterTypes, anObject, new Object[] { aParameter }); } /** * Invokes the method with the specified signature on the specified object using * the specified two parameters. */ public static Object invoke(String methodName, Class[] parameterTypes, Object anObject, Object p1, Object p2) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { return NSSelector.invoke(methodName, parameterTypes, anObject, new Object[] { p1, p2 }); } // interface Comparator private Comparator comparator; /** * Constructor specifying a method name and a comparator. This is not in the * spec. */ public NSSelector(String aMethodName, Comparator aComparator) { this(aMethodName, EMPTY_CLASS_ARRAY); comparator = aComparator; } /** * Returns the Comparator used for this selector. This is not in the spec. */ public Comparator comparator() { if (comparator == null) { comparator = new PropertyComparator(methodName); } return comparator; } public int compare(Object o1, Object o2) { if (comparator == null) { comparator = new PropertyComparator(methodName); } return comparator.compare(o1, o2); } public boolean equals(Object obj) { return (obj == this); } } /* * $Log$ Revision 1.1 2006/02/16 12:47:16 cgruber Check in all sources in * eclipse-friendly maven-enabled packages. * * Revision 1.9 2003/02/12 19:34:35 mpowers Added accessor for comparator. * * Revision 1.8 2003/02/07 20:23:41 mpowers Provided backwards compatibility for * comparators. * * Revision 1.7 2003/01/22 23:02:25 mpowers Fixed a null pointer error in * NSSelector.toString. * * Revision 1.6 2003/01/18 23:46:58 mpowers EOSortOrdering is now correctly * using NSSelectors. * * Revision 1.4 2001/10/31 15:24:45 mpowers Implicit constructor is now * protected. * * Revision 1.3 2001/02/07 19:25:51 mpowers Fixed: method matching uses * isAssignableFrom rather than ==. * * Revision 1.2 2001/01/08 23:30:16 mpowers Fixed major bug - selectors were not * supposed to share a method map. * * Revision 1.1.1.1 2000/12/21 15:47:45 mpowers Contributing wotonomy. * * Revision 1.3 2000/12/20 16:25:39 michael Added log to all files. * * */