KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > jga > swing > FunctorProxy


1 // ============================================================================
2
// $Id: FunctorProxy.java,v 1.7 2006/11/30 05:06:10 davidahall Exp $
3
// Copyright (c) 2005 David A. Hall
4
// ============================================================================
5
// The contents of this file are subject to the Common Development and
6
// Distribution License (CDDL), Version 1.0 (the License); you may not use this
7
// file except in compliance with the License. You should have received a copy
8
// of the the License along with this file: if not, a copy of the License is
9
// available from Sun Microsystems, Inc.
10
//
11
// http://www.sun.com/cddl/cddl.html
12
//
13
// From time to time, the license steward (initially Sun Microsystems, Inc.) may
14
// publish revised and/or new versions of the License. You may not use,
15
// distribute, or otherwise make this file available under subsequent versions
16
// of the License.
17
//
18
// Alternatively, the contents of this file may be used under the terms of the
19
// GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
20
// case the provisions of the LGPL are applicable instead of those above. If you
21
// wish to allow use of your version of this file only under the terms of the
22
// LGPL, and not to allow others to use your version of this file under the
23
// terms of the CDDL, indicate your decision by deleting the provisions above
24
// and replace them with the notice and other provisions required by the LGPL.
25
// If you do not delete the provisions above, a recipient may use your version
26
// of this file under the terms of either the CDDL or the LGPL.
27
//
28
// This library is distributed in the hope that it will be useful,
29
// but WITHOUT ANY WARRANTY; without even the implied warranty of
30
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
31
// ============================================================================
32

33 package net.sf.jga.swing;
34
35 import java.awt.Event JavaDoc;
36 import java.io.Serializable JavaDoc;
37 import java.lang.ref.Reference JavaDoc;
38 import java.lang.ref.WeakReference JavaDoc;
39 import java.lang.reflect.InvocationHandler JavaDoc;
40 import java.lang.reflect.InvocationTargetException JavaDoc;
41 import java.lang.reflect.Method JavaDoc;
42 import java.lang.reflect.Proxy JavaDoc;
43 import java.util.Collections JavaDoc;
44 import java.util.EventListener JavaDoc;
45 import java.util.HashMap JavaDoc;
46 import java.util.HashSet JavaDoc;
47 import java.util.Iterator JavaDoc;
48 import java.util.Map JavaDoc;
49 import java.util.Set JavaDoc;
50 import java.util.WeakHashMap JavaDoc;
51 import net.sf.jga.algorithms.Filter;
52 import net.sf.jga.fn.BinaryFunctor;
53 import net.sf.jga.fn.UnaryFunctor;
54 import net.sf.jga.parser.GenericParser;
55 import net.sf.jga.parser.ParseException;
56 import net.sf.jga.parser.UncheckedParseException;
57
58 /**
59  * Factory that builds Proxy objects that map Functors to event notification methods.
60  * Using this proxy is a two step process: first build the proxy instance calling
61  * <tt>makeListenerFor</tt> or <tt>newProxyInstance</tt>; second, register the
62  * desired functors with the various registration methods.
63  * <p>
64  * Copyright &copy; 2005 David A. Hall
65  */

66
67 public class FunctorProxy {
68
69     /**
70      * Manufactures a Proxy that can listen to any/all events that the given
71      * object may fire. The generated Proxy's invocation handler will be an
72      * instance FunctorInvocationHandler.
73      */

74     static public Proxy JavaDoc makeListenerFor(Object JavaDoc obj) {
75         return newProxyInstance(obj.getClass().getClassLoader(), identifyListeners(obj.getClass()));
76     }
77
78     
79     /**
80      * Returns an instance of a proxy class for the specified interfaces that dispatches
81      * method invocations to functors registed via a FunctorInvocationHandler.
82      */

83     static public Proxy JavaDoc newProxyInstance(ClassLoader JavaDoc loader, Class JavaDoc[] interfaces) {
84         return (Proxy JavaDoc) Proxy.newProxyInstance(loader, interfaces,
85                                               new FunctorInvocationHandler(interfaces));
86     }
87
88     // ------------
89
// registration
90
// ------------
91

92     /**
93      * Registers the functor described by the given expression, to be invoked by the proxy
94      * when a method with the given method is called. If the proxy supports more than one
95      * method with this name, then which one will be invoked is undefined.
96      *
97      * @throws NoSuchMethodException if no method with the given name is found
98      * @throws ParseException if the expression cannot be parsed
99      * @throws ClassCastException if the given proxy's handler is not a FunctorInvocationHandler
100      */

101     static public void register(Proxy JavaDoc proxy, String JavaDoc methodName, String JavaDoc expression)
102         throws ParseException, NoSuchMethodException JavaDoc
103     {
104         getFunctorInvocationHandler(proxy).register(methodName, expression);
105     }
106
107     
108     /**
109      * Registers the given functor to be invoked by the proxy when a method with the given
110      * method is called. If the proxy supports more than one method with this name, then
111      * which one will be invoked is undefined.
112      *
113      * @throws NoSuchMethodException if no method with the given name is found
114      * @throws ClassCastException if the given proxy's handler is not a FunctorInvocationHandler
115      */

116     static public void register(Proxy JavaDoc proxy, String JavaDoc methodName, UnaryFunctor functor)
117         throws NoSuchMethodException JavaDoc
118     {
119         getFunctorInvocationHandler(proxy).register(methodName, functor);
120     }
121     
122
123     /**
124      * Registers the given functor to be invoked by the proxy when the given method is called.
125      * @throws ClassCastException if the given proxy's handler is not a FunctorInvocationHandler
126      */

127     static public void register(Proxy JavaDoc proxy, Method JavaDoc method, UnaryFunctor functor) {
128         getFunctorInvocationHandler(proxy).register(method, functor);
129     }
130
131
132     /**
133      * Registers a functor to be evaluated when the given method is invoked.
134      * @throws IllegalArgumentException if the proxy does not implement the listenerClass
135      * @throws NoSuchMethodException if the proxy does not have a method with the given
136      * name that takes an argument of class eventClass
137      * @throws ClassCastException if the given proxy's handler is not a FunctorInvocationHandler
138      */

139     static public void register(Proxy JavaDoc proxy, Class JavaDoc listenerClass, Class JavaDoc eventClass,
140                                 String JavaDoc methodName, UnaryFunctor functor)
141         throws NoSuchMethodException JavaDoc, IllegalArgumentException JavaDoc
142     {
143         if (listenerClass.isInstance(proxy)) {
144             Method JavaDoc m = listenerClass.getMethod(methodName, new Class JavaDoc[]{eventClass});
145             getFunctorInvocationHandler(proxy).register(m, functor);
146             return;
147         }
148
149         throw new IllegalArgumentException JavaDoc("This does not implement " +listenerClass.getName());
150     }
151
152     
153     // ----------------------
154
// implementation details
155
// ----------------------
156

157     
158     // Maps 'Listenee' classes to the arrays of Listener interfaces they support
159
static private Map JavaDoc<Class JavaDoc<?>,WeakReference JavaDoc> proxyClasses =
160         Collections.synchronizedMap(new WeakHashMap JavaDoc<Class JavaDoc<?>,WeakReference JavaDoc>());
161
162     
163     // A predicate that is true when a method is an add${FOO}Listener
164
static private final UnaryFunctor<Method JavaDoc,Boolean JavaDoc> isAddListenerMethod =
165         GenericParser.parse("x.getName().matches(\"^add.*Listener$\")", Method JavaDoc.class, Boolean JavaDoc.class);
166
167     /**
168      * Returns a list of the ${FOO}Listener interfaces which an object of Class clasz
169      * may fire events (ie, the Listeners which such an object can add).
170      */

171     static private Class JavaDoc[] identifyListeners(Class JavaDoc<?> clasz) {
172         synchronized (proxyClasses) {
173             // See if we've already generated such a list: if so, return it.
174
Object JavaDoc value = proxyClasses.get(clasz);
175             if (value instanceof Reference JavaDoc) {
176                 return (Class JavaDoc[]) ((Reference JavaDoc) value).get();
177             }
178
179             // Otherwise, we'll need to build the list. For now, we can find all methods
180
// whose names are of the form "add${FOO}Listener", and that take a single
181
// argument of a type derived from EventListener.
182
Set JavaDoc<Class JavaDoc<?>> interfaceSet = new HashSet JavaDoc<Class JavaDoc<?>>();
183             
184 // for(Method m : Filter.filter(clasz.getMethods(), isAddListenerMethod)) {
185
Iterator JavaDoc<Method JavaDoc> iter = Filter.filter(clasz.getMethods(),isAddListenerMethod).iterator();
186             while(iter.hasNext()) {
187                 Method JavaDoc m = iter.next();
188                 Class JavaDoc[] argtypes = m.getParameterTypes();
189                 if (argtypes.length == 1) {
190                     Class JavaDoc c = m.getParameterTypes()[0];
191                     if (EventListener JavaDoc.class.isAssignableFrom(c)) {
192                         interfaceSet.add(c);
193                     }
194                 }
195             }
196
197             // Save ourselves the work next time around.
198
Class JavaDoc[] interfaceArray = interfaceSet.toArray(new Class JavaDoc[interfaceSet.size()]);
199             proxyClasses.put(clasz, new WeakReference JavaDoc(interfaceArray));
200             return interfaceArray;
201         }
202     }
203
204
205     /**
206      * @returns the FunctorInvocationHandler associated with the given proxy
207      * @throws ClassCastException if the given proxy's handler is not a FunctorInvocationHandler
208      */

209     static private FunctorInvocationHandler getFunctorInvocationHandler(Proxy JavaDoc proxy) {
210         return (FunctorInvocationHandler) Proxy.getInvocationHandler(proxy);
211     }
212
213
214     /**
215      * The InvocationHandler for FunctorProxy instances. This handler allows individual
216      * methods to be mapped to Functors of the form UnaryFunctor<Event>. When the method
217      * (which must be an event notification method of one of the listener interfaces that
218      * the proxy object implements) is invoked, the event is passed to the appropriate
219      * functor for evaluation.
220      */

221     static public class FunctorInvocationHandler implements InvocationHandler JavaDoc, Serializable JavaDoc {
222         
223         static final long serialVersionUID = -6537167128759416788L;
224
225         // The interfaces for which this handler can route method invocations
226
private Class JavaDoc[] _interfaces;
227
228         // Maps methods to the functors that will be invoked
229
private Map JavaDoc<Method JavaDoc,UnaryFunctor> _functors = new HashMap JavaDoc<Method JavaDoc,UnaryFunctor>();
230
231         // Functor that returns true if a given method has the given name
232
static private final BinaryFunctor<Method JavaDoc,String JavaDoc,Boolean JavaDoc> isNameEq =
233             GenericParser.parse("x.getName() == y", Method JavaDoc.class, String JavaDoc.class, Boolean JavaDoc.class);
234
235         private FunctorInvocationHandler(Class JavaDoc[] interfaces) {
236             _interfaces = interfaces;
237         }
238
239         
240         /**
241          * Returns the first method found with the given name
242          * @throws NoSuchMethodException if no method with the given name is found
243          */

244         private Method JavaDoc getMethod(String JavaDoc name) throws NoSuchMethodException JavaDoc {
245             UnaryFunctor<Method JavaDoc,Boolean JavaDoc> hasGivenName = isNameEq.bind2nd(name);
246
247             // Loop through the interfaces that this handler supports to find
248
// the one with the named method, and return the method if found
249

250 // for (Class iface : _interfaces)
251
for (int i = 0; i < _interfaces.length; ++i) {
252                 Class JavaDoc iface = _interfaces[i];
253                 
254 // for(Method method : Iterables.filter(iface.getMethods(), hasGivenName))
255
Iterator JavaDoc<Method JavaDoc> iter = Filter.filter(iface.getMethods(), hasGivenName).iterator();
256                 if(iter.hasNext()) {
257                     return iter.next();
258                 }
259             }
260             
261             throw new NoSuchMethodException JavaDoc(name);
262         }
263
264
265         /**
266          * Registers the functor described by the given expression, to be invoked a
267          * method with the given method is called. If this handler has more than one
268          * method with this name, then which one will be invoked is undefined.
269          *
270          * @throws NoSuchMethodException if no method with the given name is found
271          * @throws ParseException if the expression cannot be parsed
272          */

273         private void register(String JavaDoc methodName, String JavaDoc expression)
274             throws ParseException, NoSuchMethodException JavaDoc
275         {
276             Method JavaDoc m = getMethod(methodName);
277             UnaryFunctor fn =
278                 GenericParser.parse(expression, m.getParameterTypes()[0], m.getReturnType());
279             
280             register(m, fn);
281         }
282     
283     
284         /**
285          * Registers the given functor, to be invoked a method with the given method
286          * is called. If this handler has more than one method with this name, then
287          * which one will be invoked is undefined.
288          *
289          * @throws NoSuchMethodException if no method with the given name is found
290          */

291         private void register(String JavaDoc methodName, UnaryFunctor functor)
292             throws NoSuchMethodException JavaDoc
293         {
294             Method JavaDoc m = getMethod(methodName);
295             register(m, functor);
296         }
297     
298
299         /**
300          * Registers the given functor, to be invoked when the given method is called.
301          */

302         private void register(Method JavaDoc method, UnaryFunctor functor) {
303             _functors.put(method, functor);
304         }
305
306         
307         /**
308          */

309         private void unregister(Method JavaDoc method) {
310             _functors.remove(method);
311         }
312
313         
314         /**
315          */

316         private UnaryFunctor getFunctor(Method JavaDoc method){
317             return _functors.get(method);
318         }
319
320         // --------------------------------
321
// InvocationHandler Implementation
322
// --------------------------------
323

324         /**
325          */

326         public Object JavaDoc invoke(Object JavaDoc object, Method JavaDoc method, Object JavaDoc[] objectArray) throws Throwable JavaDoc {
327             UnaryFunctor functor = getFunctor(method);
328             if (functor != null) {
329                 functor.fn(objectArray[0]);
330             }
331             
332             return null;
333         }
334     }
335 }
336
Popular Tags