Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/beans/EventHandler.java
38829 views
1
/*
2
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package java.beans;
26
27
import java.lang.reflect.InvocationHandler;
28
import java.lang.reflect.InvocationTargetException;
29
import java.lang.reflect.Proxy;
30
import java.lang.reflect.Method;
31
import java.security.AccessControlContext;
32
import java.security.AccessController;
33
import java.security.PrivilegedAction;
34
35
import sun.reflect.misc.MethodUtil;
36
import sun.reflect.misc.ReflectUtil;
37
38
/**
39
* The <code>EventHandler</code> class provides
40
* support for dynamically generating event listeners whose methods
41
* execute a simple statement involving an incoming event object
42
* and a target object.
43
* <p>
44
* The <code>EventHandler</code> class is intended to be used by interactive tools, such as
45
* application builders, that allow developers to make connections between
46
* beans. Typically connections are made from a user interface bean
47
* (the event <em>source</em>)
48
* to an application logic bean (the <em>target</em>). The most effective
49
* connections of this kind isolate the application logic from the user
50
* interface. For example, the <code>EventHandler</code> for a
51
* connection from a <code>JCheckBox</code> to a method
52
* that accepts a boolean value can deal with extracting the state
53
* of the check box and passing it directly to the method so that
54
* the method is isolated from the user interface layer.
55
* <p>
56
* Inner classes are another, more general way to handle events from
57
* user interfaces. The <code>EventHandler</code> class
58
* handles only a subset of what is possible using inner
59
* classes. However, <code>EventHandler</code> works better
60
* with the long-term persistence scheme than inner classes.
61
* Also, using <code>EventHandler</code> in large applications in
62
* which the same interface is implemented many times can
63
* reduce the disk and memory footprint of the application.
64
* <p>
65
* The reason that listeners created with <code>EventHandler</code>
66
* have such a small
67
* footprint is that the <code>Proxy</code> class, on which
68
* the <code>EventHandler</code> relies, shares implementations
69
* of identical
70
* interfaces. For example, if you use
71
* the <code>EventHandler</code> <code>create</code> methods to make
72
* all the <code>ActionListener</code>s in an application,
73
* all the action listeners will be instances of a single class
74
* (one created by the <code>Proxy</code> class).
75
* In general, listeners based on
76
* the <code>Proxy</code> class require one listener class
77
* to be created per <em>listener type</em> (interface),
78
* whereas the inner class
79
* approach requires one class to be created per <em>listener</em>
80
* (object that implements the interface).
81
*
82
* <p>
83
* You don't generally deal directly with <code>EventHandler</code>
84
* instances.
85
* Instead, you use one of the <code>EventHandler</code>
86
* <code>create</code> methods to create
87
* an object that implements a given listener interface.
88
* This listener object uses an <code>EventHandler</code> object
89
* behind the scenes to encapsulate information about the
90
* event, the object to be sent a message when the event occurs,
91
* the message (method) to be sent, and any argument
92
* to the method.
93
* The following section gives examples of how to create listener
94
* objects using the <code>create</code> methods.
95
*
96
* <h2>Examples of Using EventHandler</h2>
97
*
98
* The simplest use of <code>EventHandler</code> is to install
99
* a listener that calls a method on the target object with no arguments.
100
* In the following example we create an <code>ActionListener</code>
101
* that invokes the <code>toFront</code> method on an instance
102
* of <code>javax.swing.JFrame</code>.
103
*
104
* <blockquote>
105
*<pre>
106
*myButton.addActionListener(
107
* (ActionListener)EventHandler.create(ActionListener.class, frame, "toFront"));
108
*</pre>
109
* </blockquote>
110
*
111
* When <code>myButton</code> is pressed, the statement
112
* <code>frame.toFront()</code> will be executed. One could get
113
* the same effect, with some additional compile-time type safety,
114
* by defining a new implementation of the <code>ActionListener</code>
115
* interface and adding an instance of it to the button:
116
*
117
* <blockquote>
118
*<pre>
119
//Equivalent code using an inner class instead of EventHandler.
120
*myButton.addActionListener(new ActionListener() {
121
* public void actionPerformed(ActionEvent e) {
122
* frame.toFront();
123
* }
124
*});
125
*</pre>
126
* </blockquote>
127
*
128
* The next simplest use of <code>EventHandler</code> is
129
* to extract a property value from the first argument
130
* of the method in the listener interface (typically an event object)
131
* and use it to set the value of a property in the target object.
132
* In the following example we create an <code>ActionListener</code> that
133
* sets the <code>nextFocusableComponent</code> property of the target
134
* (myButton) object to the value of the "source" property of the event.
135
*
136
* <blockquote>
137
*<pre>
138
*EventHandler.create(ActionListener.class, myButton, "nextFocusableComponent", "source")
139
*</pre>
140
* </blockquote>
141
*
142
* This would correspond to the following inner class implementation:
143
*
144
* <blockquote>
145
*<pre>
146
//Equivalent code using an inner class instead of EventHandler.
147
*new ActionListener() {
148
* public void actionPerformed(ActionEvent e) {
149
* myButton.setNextFocusableComponent((Component)e.getSource());
150
* }
151
*}
152
*</pre>
153
* </blockquote>
154
*
155
* It's also possible to create an <code>EventHandler</code> that
156
* just passes the incoming event object to the target's action.
157
* If the fourth <code>EventHandler.create</code> argument is
158
* an empty string, then the event is just passed along:
159
*
160
* <blockquote>
161
*<pre>
162
*EventHandler.create(ActionListener.class, target, "doActionEvent", "")
163
*</pre>
164
* </blockquote>
165
*
166
* This would correspond to the following inner class implementation:
167
*
168
* <blockquote>
169
*<pre>
170
//Equivalent code using an inner class instead of EventHandler.
171
*new ActionListener() {
172
* public void actionPerformed(ActionEvent e) {
173
* target.doActionEvent(e);
174
* }
175
*}
176
*</pre>
177
* </blockquote>
178
*
179
* Probably the most common use of <code>EventHandler</code>
180
* is to extract a property value from the
181
* <em>source</em> of the event object and set this value as
182
* the value of a property of the target object.
183
* In the following example we create an <code>ActionListener</code> that
184
* sets the "label" property of the target
185
* object to the value of the "text" property of the
186
* source (the value of the "source" property) of the event.
187
*
188
* <blockquote>
189
*<pre>
190
*EventHandler.create(ActionListener.class, myButton, "label", "source.text")
191
*</pre>
192
* </blockquote>
193
*
194
* This would correspond to the following inner class implementation:
195
*
196
* <blockquote>
197
*<pre>
198
//Equivalent code using an inner class instead of EventHandler.
199
*new ActionListener {
200
* public void actionPerformed(ActionEvent e) {
201
* myButton.setLabel(((JTextField)e.getSource()).getText());
202
* }
203
*}
204
*</pre>
205
* </blockquote>
206
*
207
* The event property may be "qualified" with an arbitrary number
208
* of property prefixes delimited with the "." character. The "qualifying"
209
* names that appear before the "." characters are taken as the names of
210
* properties that should be applied, left-most first, to
211
* the event object.
212
* <p>
213
* For example, the following action listener
214
*
215
* <blockquote>
216
*<pre>
217
*EventHandler.create(ActionListener.class, target, "a", "b.c.d")
218
*</pre>
219
* </blockquote>
220
*
221
* might be written as the following inner class
222
* (assuming all the properties had canonical getter methods and
223
* returned the appropriate types):
224
*
225
* <blockquote>
226
*<pre>
227
//Equivalent code using an inner class instead of EventHandler.
228
*new ActionListener {
229
* public void actionPerformed(ActionEvent e) {
230
* target.setA(e.getB().getC().isD());
231
* }
232
*}
233
*</pre>
234
* </blockquote>
235
* The target property may also be "qualified" with an arbitrary number
236
* of property prefixs delimited with the "." character. For example, the
237
* following action listener:
238
* <pre>
239
* EventHandler.create(ActionListener.class, target, "a.b", "c.d")
240
* </pre>
241
* might be written as the following inner class
242
* (assuming all the properties had canonical getter methods and
243
* returned the appropriate types):
244
* <pre>
245
* //Equivalent code using an inner class instead of EventHandler.
246
* new ActionListener {
247
* public void actionPerformed(ActionEvent e) {
248
* target.getA().setB(e.getC().isD());
249
* }
250
*}
251
*</pre>
252
* <p>
253
* As <code>EventHandler</code> ultimately relies on reflection to invoke
254
* a method we recommend against targeting an overloaded method. For example,
255
* if the target is an instance of the class <code>MyTarget</code> which is
256
* defined as:
257
* <pre>
258
* public class MyTarget {
259
* public void doIt(String);
260
* public void doIt(Object);
261
* }
262
* </pre>
263
* Then the method <code>doIt</code> is overloaded. EventHandler will invoke
264
* the method that is appropriate based on the source. If the source is
265
* null, then either method is appropriate and the one that is invoked is
266
* undefined. For that reason we recommend against targeting overloaded
267
* methods.
268
*
269
* @see java.lang.reflect.Proxy
270
* @see java.util.EventObject
271
*
272
* @since 1.4
273
*
274
* @author Mark Davidson
275
* @author Philip Milne
276
* @author Hans Muller
277
*
278
*/
279
public class EventHandler implements InvocationHandler {
280
private Object target;
281
private String action;
282
private final String eventPropertyName;
283
private final String listenerMethodName;
284
private final AccessControlContext acc = AccessController.getContext();
285
286
/**
287
* Creates a new <code>EventHandler</code> object;
288
* you generally use one of the <code>create</code> methods
289
* instead of invoking this constructor directly. Refer to
290
* {@link java.beans.EventHandler#create(Class, Object, String, String)
291
* the general version of create} for a complete description of
292
* the <code>eventPropertyName</code> and <code>listenerMethodName</code>
293
* parameter.
294
*
295
* @param target the object that will perform the action
296
* @param action the name of a (possibly qualified) property or method on
297
* the target
298
* @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
299
* @param listenerMethodName the name of the method in the listener interface that should trigger the action
300
*
301
* @throws NullPointerException if <code>target</code> is null
302
* @throws NullPointerException if <code>action</code> is null
303
*
304
* @see EventHandler
305
* @see #create(Class, Object, String, String, String)
306
* @see #getTarget
307
* @see #getAction
308
* @see #getEventPropertyName
309
* @see #getListenerMethodName
310
*/
311
@ConstructorProperties({"target", "action", "eventPropertyName", "listenerMethodName"})
312
public EventHandler(Object target, String action, String eventPropertyName, String listenerMethodName) {
313
this.target = target;
314
this.action = action;
315
if (target == null) {
316
throw new NullPointerException("target must be non-null");
317
}
318
if (action == null) {
319
throw new NullPointerException("action must be non-null");
320
}
321
this.eventPropertyName = eventPropertyName;
322
this.listenerMethodName = listenerMethodName;
323
}
324
325
/**
326
* Returns the object to which this event handler will send a message.
327
*
328
* @return the target of this event handler
329
* @see #EventHandler(Object, String, String, String)
330
*/
331
public Object getTarget() {
332
return target;
333
}
334
335
/**
336
* Returns the name of the target's writable property
337
* that this event handler will set,
338
* or the name of the method that this event handler
339
* will invoke on the target.
340
*
341
* @return the action of this event handler
342
* @see #EventHandler(Object, String, String, String)
343
*/
344
public String getAction() {
345
return action;
346
}
347
348
/**
349
* Returns the property of the event that should be
350
* used in the action applied to the target.
351
*
352
* @return the property of the event
353
*
354
* @see #EventHandler(Object, String, String, String)
355
*/
356
public String getEventPropertyName() {
357
return eventPropertyName;
358
}
359
360
/**
361
* Returns the name of the method that will trigger the action.
362
* A return value of <code>null</code> signifies that all methods in the
363
* listener interface trigger the action.
364
*
365
* @return the name of the method that will trigger the action
366
*
367
* @see #EventHandler(Object, String, String, String)
368
*/
369
public String getListenerMethodName() {
370
return listenerMethodName;
371
}
372
373
private Object applyGetters(Object target, String getters) {
374
if (getters == null || getters.equals("")) {
375
return target;
376
}
377
int firstDot = getters.indexOf('.');
378
if (firstDot == -1) {
379
firstDot = getters.length();
380
}
381
String first = getters.substring(0, firstDot);
382
String rest = getters.substring(Math.min(firstDot + 1, getters.length()));
383
384
try {
385
Method getter = null;
386
if (target != null) {
387
getter = Statement.getMethod(target.getClass(),
388
"get" + NameGenerator.capitalize(first),
389
new Class<?>[]{});
390
if (getter == null) {
391
getter = Statement.getMethod(target.getClass(),
392
"is" + NameGenerator.capitalize(first),
393
new Class<?>[]{});
394
}
395
if (getter == null) {
396
getter = Statement.getMethod(target.getClass(), first, new Class<?>[]{});
397
}
398
}
399
if (getter == null) {
400
throw new RuntimeException("No method called: " + first +
401
" defined on " + target);
402
}
403
Object newTarget = MethodUtil.invoke(getter, target, new Object[]{});
404
return applyGetters(newTarget, rest);
405
}
406
catch (Exception e) {
407
throw new RuntimeException("Failed to call method: " + first +
408
" on " + target, e);
409
}
410
}
411
412
/**
413
* Extract the appropriate property value from the event and
414
* pass it to the action associated with
415
* this <code>EventHandler</code>.
416
*
417
* @param proxy the proxy object
418
* @param method the method in the listener interface
419
* @return the result of applying the action to the target
420
*
421
* @see EventHandler
422
*/
423
public Object invoke(final Object proxy, final Method method, final Object[] arguments) {
424
AccessControlContext acc = this.acc;
425
if ((acc == null) && (System.getSecurityManager() != null)) {
426
throw new SecurityException("AccessControlContext is not set");
427
}
428
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
429
public Object run() {
430
return invokeInternal(proxy, method, arguments);
431
}
432
}, acc);
433
}
434
435
private Object invokeInternal(Object proxy, Method method, Object[] arguments) {
436
String methodName = method.getName();
437
if (method.getDeclaringClass() == Object.class) {
438
// Handle the Object public methods.
439
if (methodName.equals("hashCode")) {
440
return new Integer(System.identityHashCode(proxy));
441
} else if (methodName.equals("equals")) {
442
return (proxy == arguments[0] ? Boolean.TRUE : Boolean.FALSE);
443
} else if (methodName.equals("toString")) {
444
return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode());
445
}
446
}
447
448
if (listenerMethodName == null || listenerMethodName.equals(methodName)) {
449
Class[] argTypes = null;
450
Object[] newArgs = null;
451
452
if (eventPropertyName == null) { // Nullary method.
453
newArgs = new Object[]{};
454
argTypes = new Class<?>[]{};
455
}
456
else {
457
Object input = applyGetters(arguments[0], getEventPropertyName());
458
newArgs = new Object[]{input};
459
argTypes = new Class<?>[]{input == null ? null :
460
input.getClass()};
461
}
462
try {
463
int lastDot = action.lastIndexOf('.');
464
if (lastDot != -1) {
465
target = applyGetters(target, action.substring(0, lastDot));
466
action = action.substring(lastDot + 1);
467
}
468
Method targetMethod = Statement.getMethod(
469
target.getClass(), action, argTypes);
470
if (targetMethod == null) {
471
targetMethod = Statement.getMethod(target.getClass(),
472
"set" + NameGenerator.capitalize(action), argTypes);
473
}
474
if (targetMethod == null) {
475
String argTypeString = (argTypes.length == 0)
476
? " with no arguments"
477
: " with argument " + argTypes[0];
478
throw new RuntimeException(
479
"No method called " + action + " on " +
480
target.getClass() + argTypeString);
481
}
482
return MethodUtil.invoke(targetMethod, target, newArgs);
483
}
484
catch (IllegalAccessException ex) {
485
throw new RuntimeException(ex);
486
}
487
catch (InvocationTargetException ex) {
488
Throwable th = ex.getTargetException();
489
throw (th instanceof RuntimeException)
490
? (RuntimeException) th
491
: new RuntimeException(th);
492
}
493
}
494
return null;
495
}
496
497
/**
498
* Creates an implementation of <code>listenerInterface</code> in which
499
* <em>all</em> of the methods in the listener interface apply
500
* the handler's <code>action</code> to the <code>target</code>. This
501
* method is implemented by calling the other, more general,
502
* implementation of the <code>create</code> method with both
503
* the <code>eventPropertyName</code> and the <code>listenerMethodName</code>
504
* taking the value <code>null</code>. Refer to
505
* {@link java.beans.EventHandler#create(Class, Object, String, String)
506
* the general version of create} for a complete description of
507
* the <code>action</code> parameter.
508
* <p>
509
* To create an <code>ActionListener</code> that shows a
510
* <code>JDialog</code> with <code>dialog.show()</code>,
511
* one can write:
512
*
513
*<blockquote>
514
*<pre>
515
*EventHandler.create(ActionListener.class, dialog, "show")
516
*</pre>
517
*</blockquote>
518
*
519
* @param <T> the type to create
520
* @param listenerInterface the listener interface to create a proxy for
521
* @param target the object that will perform the action
522
* @param action the name of a (possibly qualified) property or method on
523
* the target
524
* @return an object that implements <code>listenerInterface</code>
525
*
526
* @throws NullPointerException if <code>listenerInterface</code> is null
527
* @throws NullPointerException if <code>target</code> is null
528
* @throws NullPointerException if <code>action</code> is null
529
*
530
* @see #create(Class, Object, String, String)
531
*/
532
public static <T> T create(Class<T> listenerInterface,
533
Object target, String action)
534
{
535
return create(listenerInterface, target, action, null, null);
536
}
537
538
/**
539
/**
540
* Creates an implementation of <code>listenerInterface</code> in which
541
* <em>all</em> of the methods pass the value of the event
542
* expression, <code>eventPropertyName</code>, to the final method in the
543
* statement, <code>action</code>, which is applied to the <code>target</code>.
544
* This method is implemented by calling the
545
* more general, implementation of the <code>create</code> method with
546
* the <code>listenerMethodName</code> taking the value <code>null</code>.
547
* Refer to
548
* {@link java.beans.EventHandler#create(Class, Object, String, String)
549
* the general version of create} for a complete description of
550
* the <code>action</code> and <code>eventPropertyName</code> parameters.
551
* <p>
552
* To create an <code>ActionListener</code> that sets the
553
* the text of a <code>JLabel</code> to the text value of
554
* the <code>JTextField</code> source of the incoming event,
555
* you can use the following code:
556
*
557
*<blockquote>
558
*<pre>
559
*EventHandler.create(ActionListener.class, label, "text", "source.text");
560
*</pre>
561
*</blockquote>
562
*
563
* This is equivalent to the following code:
564
*<blockquote>
565
*<pre>
566
//Equivalent code using an inner class instead of EventHandler.
567
*new ActionListener() {
568
* public void actionPerformed(ActionEvent event) {
569
* label.setText(((JTextField)(event.getSource())).getText());
570
* }
571
*};
572
*</pre>
573
*</blockquote>
574
*
575
* @param <T> the type to create
576
* @param listenerInterface the listener interface to create a proxy for
577
* @param target the object that will perform the action
578
* @param action the name of a (possibly qualified) property or method on
579
* the target
580
* @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
581
*
582
* @return an object that implements <code>listenerInterface</code>
583
*
584
* @throws NullPointerException if <code>listenerInterface</code> is null
585
* @throws NullPointerException if <code>target</code> is null
586
* @throws NullPointerException if <code>action</code> is null
587
*
588
* @see #create(Class, Object, String, String, String)
589
*/
590
public static <T> T create(Class<T> listenerInterface,
591
Object target, String action,
592
String eventPropertyName)
593
{
594
return create(listenerInterface, target, action, eventPropertyName, null);
595
}
596
597
/**
598
* Creates an implementation of <code>listenerInterface</code> in which
599
* the method named <code>listenerMethodName</code>
600
* passes the value of the event expression, <code>eventPropertyName</code>,
601
* to the final method in the statement, <code>action</code>, which
602
* is applied to the <code>target</code>. All of the other listener
603
* methods do nothing.
604
* <p>
605
* The <code>eventPropertyName</code> string is used to extract a value
606
* from the incoming event object that is passed to the target
607
* method. The common case is the target method takes no arguments, in
608
* which case a value of null should be used for the
609
* <code>eventPropertyName</code>. Alternatively if you want
610
* the incoming event object passed directly to the target method use
611
* the empty string.
612
* The format of the <code>eventPropertyName</code> string is a sequence of
613
* methods or properties where each method or
614
* property is applied to the value returned by the preceding method
615
* starting from the incoming event object.
616
* The syntax is: <code>propertyName{.propertyName}*</code>
617
* where <code>propertyName</code> matches a method or
618
* property. For example, to extract the <code>point</code>
619
* property from a <code>MouseEvent</code>, you could use either
620
* <code>"point"</code> or <code>"getPoint"</code> as the
621
* <code>eventPropertyName</code>. To extract the "text" property from
622
* a <code>MouseEvent</code> with a <code>JLabel</code> source use any
623
* of the following as <code>eventPropertyName</code>:
624
* <code>"source.text"</code>,
625
* <code>"getSource.text"</code> <code>"getSource.getText"</code> or
626
* <code>"source.getText"</code>. If a method can not be found, or an
627
* exception is generated as part of invoking a method a
628
* <code>RuntimeException</code> will be thrown at dispatch time. For
629
* example, if the incoming event object is null, and
630
* <code>eventPropertyName</code> is non-null and not empty, a
631
* <code>RuntimeException</code> will be thrown.
632
* <p>
633
* The <code>action</code> argument is of the same format as the
634
* <code>eventPropertyName</code> argument where the last property name
635
* identifies either a method name or writable property.
636
* <p>
637
* If the <code>listenerMethodName</code> is <code>null</code>
638
* <em>all</em> methods in the interface trigger the <code>action</code> to be
639
* executed on the <code>target</code>.
640
* <p>
641
* For example, to create a <code>MouseListener</code> that sets the target
642
* object's <code>origin</code> property to the incoming <code>MouseEvent</code>'s
643
* location (that's the value of <code>mouseEvent.getPoint()</code>) each
644
* time a mouse button is pressed, one would write:
645
*<blockquote>
646
*<pre>
647
*EventHandler.create(MouseListener.class, target, "origin", "point", "mousePressed");
648
*</pre>
649
*</blockquote>
650
*
651
* This is comparable to writing a <code>MouseListener</code> in which all
652
* of the methods except <code>mousePressed</code> are no-ops:
653
*
654
*<blockquote>
655
*<pre>
656
//Equivalent code using an inner class instead of EventHandler.
657
*new MouseAdapter() {
658
* public void mousePressed(MouseEvent e) {
659
* target.setOrigin(e.getPoint());
660
* }
661
*};
662
* </pre>
663
*</blockquote>
664
*
665
* @param <T> the type to create
666
* @param listenerInterface the listener interface to create a proxy for
667
* @param target the object that will perform the action
668
* @param action the name of a (possibly qualified) property or method on
669
* the target
670
* @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
671
* @param listenerMethodName the name of the method in the listener interface that should trigger the action
672
*
673
* @return an object that implements <code>listenerInterface</code>
674
*
675
* @throws NullPointerException if <code>listenerInterface</code> is null
676
* @throws NullPointerException if <code>target</code> is null
677
* @throws NullPointerException if <code>action</code> is null
678
*
679
* @see EventHandler
680
*/
681
public static <T> T create(Class<T> listenerInterface,
682
Object target, String action,
683
String eventPropertyName,
684
String listenerMethodName)
685
{
686
// Create this first to verify target/action are non-null
687
final EventHandler handler = new EventHandler(target, action,
688
eventPropertyName,
689
listenerMethodName);
690
if (listenerInterface == null) {
691
throw new NullPointerException(
692
"listenerInterface must be non-null");
693
}
694
final ClassLoader loader = getClassLoader(listenerInterface);
695
final Class<?>[] interfaces = {listenerInterface};
696
return AccessController.doPrivileged(new PrivilegedAction<T>() {
697
@SuppressWarnings("unchecked")
698
public T run() {
699
return (T) Proxy.newProxyInstance(loader, interfaces, handler);
700
}
701
});
702
}
703
704
private static ClassLoader getClassLoader(Class<?> type) {
705
ReflectUtil.checkPackageAccess(type);
706
ClassLoader loader = type.getClassLoader();
707
if (loader == null) {
708
loader = Thread.currentThread().getContextClassLoader(); // avoid use of BCP
709
if (loader == null) {
710
loader = ClassLoader.getSystemClassLoader();
711
}
712
}
713
return loader;
714
}
715
}
716
717