KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > script > rhino > EventTargetWrapper


1 /*
2
3    Copyright 2001-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.script.rhino;
19
20 import java.lang.ref.SoftReference JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.WeakHashMap JavaDoc;
25
26 import org.mozilla.javascript.Context;
27 import org.mozilla.javascript.Function;
28 import org.mozilla.javascript.JavaScriptException;
29 import org.mozilla.javascript.NativeJavaObject;
30 import org.mozilla.javascript.NativeObject;
31 import org.mozilla.javascript.Scriptable;
32 import org.mozilla.javascript.ScriptableObject;
33 import org.mozilla.javascript.Undefined;
34 import org.mozilla.javascript.WrappedException;
35 import org.w3c.dom.events.Event JavaDoc;
36 import org.w3c.dom.events.EventListener JavaDoc;
37 import org.w3c.dom.events.EventTarget JavaDoc;
38
39 /**
40  * A class that wraps an <code>EventTarget</code> instance to expose
41  * it in the Rhino engine. Then calling <code>addEventListener</code>
42  * with a Rhino function as parameter should redirect the call to
43  * <code>addEventListener</code> with a Java function object calling
44  * the Rhino function.
45  * This class also allows to pass an ECMAScript (Rhino) object as
46  * a parameter instead of a function provided the fact that this object
47  * has a <code>handleEvent</code> method.
48  * @author <a HREF="mailto:cjolif@ilog.fr">Christophe Jolif</a>
49  * @version $Id: EventTargetWrapper.java,v 1.18 2005/02/22 09:13:02 cam Exp $
50  */

51 class EventTargetWrapper extends NativeJavaObject {
52
53     /**
54      * The Java function object calling the Rhino function.
55      */

56     static class FunctionEventListener implements EventListener JavaDoc {
57         private Function function;
58         private RhinoInterpreter interpreter;
59         FunctionEventListener(Function f, RhinoInterpreter i) {
60             function = f;
61             interpreter = i;
62         }
63         public void handleEvent(Event JavaDoc evt) {
64             try {
65                 interpreter.callHandler(function, evt);
66             } catch (JavaScriptException e) {
67                 // the only simple solution is to forward it as a
68
// RuntimeException to be catched by event dispatching
69
// in BridgeEventSupport.java
70
// another solution will to give UserAgent to interpreters
71
throw new WrappedException(e);
72             }
73         }
74     }
75
76     static class HandleEventListener implements EventListener JavaDoc {
77         private final static String JavaDoc HANDLE_EVENT = "handleEvent";
78
79         private Scriptable scriptable;
80         private Object JavaDoc[] array = new Object JavaDoc[1];
81         private RhinoInterpreter interpreter;
82
83         HandleEventListener(Scriptable s, RhinoInterpreter interpreter) {
84             scriptable = s;
85             this.interpreter = interpreter;
86         }
87         public void handleEvent(Event JavaDoc evt) {
88             try {
89                 array[0] = evt;
90                 interpreter.enterContext();
91                 ScriptableObject.callMethod(scriptable, HANDLE_EVENT, array);
92             } catch (JavaScriptException e) {
93                 // the only simple solution is to forward it as a
94
// RuntimeException to be catched by event dispatching
95
// in BridgeEventSupport.java
96
// another solution will to give UserAgent to interpreters
97
throw new WrappedException(e);
98             } finally {
99                 Context.exit();
100             }
101         }
102     }
103
104     static abstract class FunctionProxy implements Function {
105         protected Function delegate;
106
107         public FunctionProxy(Function delegate) {
108             this.delegate = delegate;
109         }
110
111         public Scriptable construct(Context cx,
112                                     Scriptable scope, Object JavaDoc[] args)
113             throws JavaScriptException {
114             return this.delegate.construct(cx, scope, args);
115         }
116
117         public String JavaDoc getClassName() {
118             return this.delegate.getClassName();
119         }
120
121         public Object JavaDoc get(String JavaDoc name, Scriptable start) {
122             return this.delegate.get(name, start);
123         }
124
125         public Object JavaDoc get(int index, Scriptable start) {
126             return this.delegate.get(index, start);
127         }
128
129         public boolean has(String JavaDoc name, Scriptable start) {
130             return this.delegate.has(name, start);
131         }
132
133         public boolean has(int index, Scriptable start) {
134             return this.delegate.has(index, start);
135         }
136
137         public void put(String JavaDoc name, Scriptable start, Object JavaDoc value) {
138             this.delegate.put(name, start, value);
139         }
140
141         public void put(int index, Scriptable start, Object JavaDoc value) {
142             this.delegate.put(index, start, value);
143         }
144
145         public void delete(String JavaDoc name) {
146             this.delegate.delete(name);
147         }
148
149         public void delete(int index) {
150             this.delegate.delete(index);
151         }
152
153         public Scriptable getPrototype() {
154             return this.delegate.getPrototype();
155         }
156
157         public void setPrototype(Scriptable prototype) {
158             this.delegate.setPrototype(prototype);
159         }
160
161         public Scriptable getParentScope() {
162             return this.delegate.getParentScope();
163         }
164
165         public void setParentScope(Scriptable parent) {
166             this.delegate.setParentScope(parent);
167         }
168
169         public Object JavaDoc[] getIds() {
170             return this.delegate.getIds();
171         }
172
173         public Object JavaDoc getDefaultValue(Class JavaDoc hint) {
174             return this.delegate.getDefaultValue(hint);
175         }
176
177         public boolean hasInstance(Scriptable instance) {
178             return this.delegate.hasInstance(instance);
179         }
180     }
181
182     /**
183      * This function proxy is delegating most of the job
184      * to the underlying NativeJavaMethod object through
185      * the FunctionProxy. However to allow user to specify
186      * "Function" or objects with an "handleEvent" method
187      * as parameter of "addEventListener"
188      * it redefines the call method to deal with these
189      * cases.
190      */

191     static class FunctionAddProxy extends FunctionProxy {
192         private Map JavaDoc listenerMap;
193
194         FunctionAddProxy(Function delegate, Map JavaDoc listenerMap) {
195             super(delegate);
196             this.listenerMap = listenerMap;
197         }
198
199         public Object JavaDoc call(Context ctx, Scriptable scope,
200                            Scriptable thisObj, Object JavaDoc[] args)
201             throws JavaScriptException {
202             NativeJavaObject njo = (NativeJavaObject)thisObj;
203             if (args[1] instanceof Function) {
204                 EventListener JavaDoc evtListener = new FunctionEventListener
205                     ((Function)args[1],
206                      ((RhinoInterpreter.ExtendedContext)ctx).getInterpreter());
207                 listenerMap.put(args[1], new SoftReference JavaDoc(evtListener));
208                 // we need to marshall args
209
Class JavaDoc[] paramTypes = { String JavaDoc.class, Function.class,
210                                        Boolean.TYPE };
211                 for (int i = 0; i < args.length; i++)
212                     args[i] = Context.toType(args[i], paramTypes[i]);
213                 ((EventTarget JavaDoc)njo.unwrap()).addEventListener
214                     ((String JavaDoc)args[0], evtListener,
215                      ((Boolean JavaDoc)args[2]).booleanValue());
216                 return Undefined.instance;
217             }
218             if (args[1] instanceof NativeObject) {
219                 EventListener JavaDoc evtListener =
220                     new HandleEventListener((Scriptable)args[1],
221                                             ((RhinoInterpreter.ExtendedContext)
222                                              ctx).getInterpreter());
223                 listenerMap.put(args[1], new SoftReference JavaDoc(evtListener));
224                 // we need to marshall args
225
Class JavaDoc[] paramTypes = { String JavaDoc.class, Scriptable.class,
226                                        Boolean.TYPE };
227                 for (int i = 0; i < args.length; i++)
228                     args[i] = Context.toType(args[i], paramTypes[i]);
229                 ((EventTarget JavaDoc)njo.unwrap()).addEventListener
230                     ((String JavaDoc)args[0], evtListener,
231                      ((Boolean JavaDoc)args[2]).booleanValue());
232                 return Undefined.instance;
233             }
234             return delegate.call(ctx, scope, thisObj, args);
235         }
236     }
237
238     static class FunctionRemoveProxy extends FunctionProxy {
239         private Map JavaDoc listenerMap;
240
241         FunctionRemoveProxy(Function delegate, Map JavaDoc listenerMap) {
242             super(delegate);
243             this.listenerMap = listenerMap;
244         }
245
246         public Object JavaDoc call(Context ctx, Scriptable scope,
247                            Scriptable thisObj, Object JavaDoc[] args)
248             throws JavaScriptException {
249             NativeJavaObject njo = (NativeJavaObject)thisObj;
250             if (args[1] instanceof Function) {
251                 SoftReference JavaDoc sr = (SoftReference JavaDoc)listenerMap.get(args[1]);
252                 if (sr == null)
253                     return Undefined.instance;
254                 EventListener JavaDoc el = (EventListener JavaDoc)sr.get();
255                 if (el == null)
256                     return Undefined.instance;
257                 // we need to marshall args
258
Class JavaDoc[] paramTypes = { String JavaDoc.class, Function.class,
259                                        Boolean.TYPE };
260                 for (int i = 0; i < args.length; i++)
261                     args[i] = Context.toType(args[i], paramTypes[i]);
262                 ((EventTarget JavaDoc)njo.unwrap()).removeEventListener
263                     ((String JavaDoc)args[0], el, ((Boolean JavaDoc)args[2]).booleanValue());
264                 return Undefined.instance;
265             }
266             if (args[1] instanceof NativeObject) {
267                 SoftReference JavaDoc sr = (SoftReference JavaDoc)listenerMap.get(args[1]);
268                 if (sr == null)
269                     return Undefined.instance;
270                 EventListener JavaDoc el = (EventListener JavaDoc)sr.get();
271                 if (el == null)
272                     return Undefined.instance;
273                 // we need to marshall args
274
Class JavaDoc[] paramTypes = { String JavaDoc.class, Scriptable.class,
275                                        Boolean.TYPE };
276                 for (int i = 0; i < args.length; i++)
277                     args[i] = Context.toType(args[i], paramTypes[i]);
278
279                 ((EventTarget JavaDoc)njo.unwrap()).removeEventListener
280                     ((String JavaDoc)args[0], el, ((Boolean JavaDoc)args[2]).booleanValue());
281                 return Undefined.instance;
282             }
283             return delegate.call(ctx, scope, thisObj, args);
284         }
285     }
286
287     // the keys are the underlying Java object, in order
288
// to remove potential memory leaks use a WeakHashMap to allow
289
// to collect entries as soon as the underlying Java object is
290
// not anymore available.
291
private static WeakHashMap JavaDoc mapOfListenerMap;
292
293     private final static String JavaDoc ADD_NAME = "addEventListener";
294     private final static String JavaDoc REMOVE_NAME = "removeEventListener";
295     private final static Class JavaDoc[] ARGS_TYPE = { String JavaDoc.class,
296                                                EventListener JavaDoc.class,
297                                                Boolean.TYPE };
298     private final static String JavaDoc NAME = "name";
299
300     EventTargetWrapper(Scriptable scope, EventTarget JavaDoc object) {
301         super(scope, object, null);
302     }
303
304     /**
305      * Overriden Rhino method.
306      */

307     public Object JavaDoc get(String JavaDoc name, Scriptable start) {
308         Object JavaDoc method = super.get(name, start);
309         if (name.equals(ADD_NAME)) {
310             // prevent creating a Map for all JavaScript objects
311
// when we need it only from time to time...
312
method = new FunctionAddProxy((Function)method, initMap());
313         }
314         if (name.equals(REMOVE_NAME)) {
315             // prevent creating a Map for all JavaScript objects
316
// when we need it only from time to time...
317
method = new FunctionRemoveProxy((Function)method, initMap());
318         }
319         return method;
320     }
321
322     // we have to store the listenerMap in a Map because
323
// several EventTargetWrapper may be created for the exact
324
// same underlying Java object.
325
public Map JavaDoc initMap() {
326         Map JavaDoc map = null;
327         if (mapOfListenerMap == null)
328             mapOfListenerMap = new WeakHashMap JavaDoc(10);
329         if ((map = (Map JavaDoc)mapOfListenerMap.get(unwrap())) == null) {
330             mapOfListenerMap.put(unwrap(), map = new WeakHashMap JavaDoc(2));
331         }
332         return map;
333     }
334 }
335
Popular Tags