View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Blacksmith, Inc.
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
17  */
18  
19  package net.wotonomy.foundation;
20  
21  import java.io.Serializable;
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  import java.util.Comparator;
25  
26  import net.wotonomy.foundation.internal.PropertyComparator;
27  
28  /***
29  * A pure java implementation of NSSelector.
30  *
31  * @author michael@mpowers.net
32  * @author $Author: cgruber $
33  * @version $Revision: 892 $
34  */
35  public class NSSelector implements Comparator, Serializable
36  {
37  	protected NSMutableDictionary methodMap; // map of classes to methods
38  	protected String methodName;
39  	protected Class[] parameterTypes;
40  
41  	/***
42  	* A marker to indicate object not found.
43  	*/
44  	protected static final String NOT_FOUND = "NOT_FOUND";
45  	
46  	/***
47  	* Saves creating a new class array for parameterless method invocation.
48  	*/
49  	protected static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
50  	
51  	/***
52  	* Saves creating a new object array for parameterless method invocation.
53  	*/
54  	protected static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
55  	
56      /***
57      * Constructor specifying a method name and an array of parameter types.
58      */
59      public NSSelector (String aMethodName, Class[] aParameterTypeArray)
60      {
61      	methodName = aMethodName;
62  	    parameterTypes = aParameterTypeArray;
63  	    methodMap = new NSMutableDictionary();
64      }
65      
66      /***
67      * Constructor specifying a method name with no parameters.
68      */
69      public NSSelector (String aMethodName)
70      {
71      	this( aMethodName, EMPTY_CLASS_ARRAY );
72      }
73      
74      /***
75      * Constructor for custom subclasses that implement specific operators
76      * and that do not use dynamic method invocation.
77      */
78      protected NSSelector()
79      {
80      }
81  
82      /***
83      * Returns the name of the method.
84      */
85      public String name ()
86      {
87      	return methodName;
88      }
89  
90      /***
91      * Returns the array of parameter types.
92      */
93      public Class[] parameterTypes ()
94      {
95      	return parameterTypes;
96      }
97  
98      /***
99      * A String description of this selector.
100     */
101     public String toString ()
102     {
103     	StringBuffer result = new StringBuffer();
104 	    result.append( "[" + getClass().getName() + ": name = " + name() + ", parameter types = [" );
105         if ( parameterTypes != null )
106         {
107             if ( parameterTypes.length > 0 )
108             {
109                 result.append( parameterTypes[0].toString() );
110             }
111             for ( int i = 1; i < parameterTypes.length; i++ )
112             {
113                 result.append( ", " );
114                 result.append( parameterTypes[i].toString() );
115             }
116         }
117 	    result.append( "] ]" );
118 	    return result.toString();
119     }
120 
121     /***
122     * Returns the appropriate method for the specified class.
123     */
124     public Method methodOnClass (Class aClass) 
125     	throws NoSuchMethodException
126 	{
127 		Object result = methodMap.objectForKey( aClass );
128 		
129 		if ( result == null )
130 		{
131 			result = getMethodForClass( aClass );
132             if ( result == null )
133             {
134                 result = NOT_FOUND;
135             }
136 			methodMap.setObjectForKey( result, aClass );
137 		}
138 		
139 		if ( result == NOT_FOUND )
140 		{
141 			throw new NoSuchMethodException();
142 		}
143 		return (Method) result;
144 	}
145 	
146     /***
147     * Returns the appropriate method, or null if not found.
148     */
149 	private Method getMethodForClass( Class aClass )
150 	{
151 		Method[] methods = aClass.getMethods();
152 		for ( int i = 0; i < methods.length; i++ )
153 		{
154 			if ( methods[i].getName().equals( name() ) )
155 			{
156 				Class[] params = methods[i].getParameterTypes();
157 				if ( params.length == parameterTypes.length )
158 				{
159 					boolean pass = true;
160 					for ( int j = 0; j < params.length; j++ )
161 					{
162 						if ( ! params[j].isAssignableFrom( parameterTypes[j] ) )
163 						{
164 							pass = false;
165 						}
166 					}	
167 					if ( pass ) return methods[i];
168 				}
169 			}
170 		}
171 		return null;
172 	}
173 
174     /***
175     * Convenience to get a method for an object.
176     */
177     public Method methodOnObject (Object anObject)
178     	throws NoSuchMethodException
179 	{
180 		Method m = methodOnClass( anObject.getClass() );
181         if ( m == null ) throw new NoSuchMethodException( name() );
182         return m;
183 	}
184 
185     /***
186     * Returns whether the class implements the method for this selector.
187     */
188     public boolean implementedByClass (Class aClass)
189     {
190     	try
191 	    {
192 			methodOnClass( aClass );
193 			return true;
194 	    }
195 	    catch ( NoSuchMethodException exc )
196 	    {
197 	    }
198 	    return false;
199     }
200 
201     /***
202     * Returns whether the object's class implements the method 
203     * for this selector.
204     */
205     public boolean implementedByObject (Object anObject)
206     {
207     	try
208 	    {
209 			methodOnObject( anObject );
210 			return true;
211 	    }
212 	    catch ( NoSuchMethodException exc )
213 	    {
214 	    }
215 	    return false;
216     }
217 
218     /***
219     * Invokes this selector's method on the specified object 
220     * using the specified parameters.
221     */
222     public Object invoke (Object anObject, Object[] parameters) 
223     	throws IllegalAccessException, IllegalArgumentException, 
224 	    	InvocationTargetException, NoSuchMethodException
225 	{
226 		return methodOnObject( anObject ).invoke( anObject, parameters );
227 	}
228 
229     /***
230     * Invokes this selector's method on the specified object 
231     * with no parameters.
232     */
233     public Object invoke (Object anObject) 
234     	throws IllegalAccessException, IllegalArgumentException, 
235 	    	InvocationTargetException, NoSuchMethodException
236 	{
237 		return invoke( anObject, EMPTY_OBJECT_ARRAY );
238 	}
239 
240     /***
241     * Invokes this selector's method on the specified object 
242     * with the specified parameter.
243     */
244     public Object invoke (Object anObject, Object aParameter) 
245     	throws IllegalAccessException, IllegalArgumentException, 
246 	    	InvocationTargetException, NoSuchMethodException
247 	{
248 		return invoke( anObject, new Object[] { aParameter } );
249 	}
250 
251     /***
252     * Invokes this selector's method on the specified object 
253     * using the specified two parameters.
254     */
255     public Object invoke (Object anObject, Object p1, Object p2) 
256     	throws IllegalAccessException, IllegalArgumentException, 
257 	    	InvocationTargetException, NoSuchMethodException
258 	{
259 		return invoke( anObject, new Object[] { p1, p2 } );
260 	}
261 		    
262     /***
263     * Invokes the method with the specified signature on the specified 
264     * object using the specified parameters.
265     */
266     public static Object invoke 
267 		(String methodName, Class[] parameterTypes, Object anObject, Object[] parameters) 
268     	throws IllegalAccessException, IllegalArgumentException, 
269 	    	InvocationTargetException, NoSuchMethodException
270 	{
271 		return new NSSelector( methodName, parameterTypes ).invoke( anObject, parameters );
272 	}
273 
274     /***
275     * Invokes the method with the specified signature on the specified object 
276     * with no parameters.
277     */
278     public static Object invoke  
279 		(String methodName, Object anObject) 
280     	throws IllegalAccessException, IllegalArgumentException, 
281 	    	InvocationTargetException, NoSuchMethodException
282 	{
283 		return NSSelector.invoke( 
284 			methodName, EMPTY_CLASS_ARRAY, anObject, EMPTY_OBJECT_ARRAY );
285 	}
286 
287     /***
288     * Invokes the method with the specified signature on the specified 
289     * object using the specified parameter.
290     */
291     public static Object invoke  
292 		(String methodName, Class[] parameterTypes, 
293 		 Object anObject, Object aParameter) 
294     	throws IllegalAccessException, IllegalArgumentException, 
295 	    	InvocationTargetException, NoSuchMethodException
296 	{
297 		return NSSelector.invoke( 
298 			methodName, parameterTypes, anObject, new Object[] { aParameter } );
299 	}
300 
301     /***
302     * Invokes the method with the specified signature on the specified 
303     * object using the specified two parameters.
304     */
305     public static Object invoke  
306 		(String methodName, Class[] parameterTypes, 
307 		 Object anObject, Object p1, Object p2) 
308     	throws IllegalAccessException, IllegalArgumentException, 
309 	    	InvocationTargetException, NoSuchMethodException
310 	{
311 		return NSSelector.invoke( 
312 			methodName, parameterTypes, anObject, new Object[] { p1, p2 } );
313 	}
314 
315     // interface Comparator
316 
317     private Comparator comparator;	
318     
319     /***
320     * Constructor specifying a method name and a comparator.
321     * This is not in the spec.
322     */
323     public NSSelector (String aMethodName, Comparator aComparator)
324     {
325     	this( aMethodName, EMPTY_CLASS_ARRAY );
326         comparator = aComparator;
327     }
328     
329     /***
330     * Returns the Comparator used for this selector.
331     * This is not in the spec.
332     */
333     public Comparator comparator()
334     {
335         if ( comparator == null )
336         {
337             comparator = new PropertyComparator( methodName );
338         }
339         return comparator;
340     }
341     
342     public int compare(Object o1, Object o2)
343     {
344         if ( comparator == null )
345         {
346             comparator = new PropertyComparator( methodName );
347         }
348         return comparator.compare( o1, o2 );
349     }
350     
351     public boolean equals(Object obj)
352     {
353         return ( obj == this );
354     }
355 }
356 
357 /*
358  * $Log$
359  * Revision 1.1  2006/02/16 12:47:16  cgruber
360  * Check in all sources in eclipse-friendly maven-enabled packages.
361  *
362  * Revision 1.9  2003/02/12 19:34:35  mpowers
363  * Added accessor for comparator.
364  *
365  * Revision 1.8  2003/02/07 20:23:41  mpowers
366  * Provided backwards compatibility for comparators.
367  *
368  * Revision 1.7  2003/01/22 23:02:25  mpowers
369  * Fixed a null pointer error in NSSelector.toString.
370  *
371  * Revision 1.6  2003/01/18 23:46:58  mpowers
372  * EOSortOrdering is now correctly using NSSelectors.
373  *
374  * Revision 1.4  2001/10/31 15:24:45  mpowers
375  * Implicit constructor is now protected.
376  *
377  * Revision 1.3  2001/02/07 19:25:51  mpowers
378  * Fixed: method matching uses isAssignableFrom rather than ==.
379  *
380  * Revision 1.2  2001/01/08 23:30:16  mpowers
381  * Fixed major bug - selectors were not supposed to share a method map.
382  *
383  * Revision 1.1.1.1  2000/12/21 15:47:45  mpowers
384  * Contributing wotonomy.
385  *
386  * Revision 1.3  2000/12/20 16:25:39  michael
387  * Added log to all files.
388  *
389  *
390  */
391