1 /*
2 * Copyright 1996-2008 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package java.beans;
27
28 import java.lang.ref.Reference;
29
30 import java.lang.reflect.Method;
31
32 /**
33 * An EventSetDescriptor describes a group of events that a given Java
34 * bean fires.
35 * <P>
36 * The given group of events are all delivered as method calls on a single
37 * event listener interface, and an event listener object can be registered
38 * via a call on a registration method supplied by the event source.
39 */
40 public class EventSetDescriptor extends FeatureDescriptor {
41
42 private MethodDescriptor[] listenerMethodDescriptors;
43 private MethodDescriptor addMethodDescriptor;
44 private MethodDescriptor removeMethodDescriptor;
45 private MethodDescriptor getMethodDescriptor;
46
47 private Reference<Method[]> listenerMethodsRef;
48 private Reference<Class> listenerTypeRef;
49
50 private boolean unicast;
51 private boolean inDefaultEventSet = true;
52
53 /**
54 * Creates an <TT>EventSetDescriptor</TT> assuming that you are
55 * following the most simple standard design pattern where a named
56 * event "fred" is (1) delivered as a call on the single method of
57 * interface FredListener, (2) has a single argument of type FredEvent,
58 * and (3) where the FredListener may be registered with a call on an
59 * addFredListener method of the source component and removed with a
60 * call on a removeFredListener method.
61 *
62 * @param sourceClass The class firing the event.
63 * @param eventSetName The programmatic name of the event. E.g. "fred".
64 * Note that this should normally start with a lower-case character.
65 * @param listenerType The target interface that events
66 * will get delivered to.
67 * @param listenerMethodName The method that will get called when the event gets
68 * delivered to its target listener interface.
69 * @exception IntrospectionException if an exception occurs during
70 * introspection.
71 */
72 public EventSetDescriptor(Class<?> sourceClass, String eventSetName,
73 Class<?> listenerType, String listenerMethodName)
74 throws IntrospectionException {
75 this(sourceClass, eventSetName, listenerType,
76 new String[] { listenerMethodName },
77 Introspector.ADD_PREFIX + getListenerClassName(listenerType),
78 Introspector.REMOVE_PREFIX + getListenerClassName(listenerType),
79 Introspector.GET_PREFIX + getListenerClassName(listenerType) + "s");
80
81 String eventName = NameGenerator.capitalize(eventSetName) + "Event";
82 Method[] listenerMethods = getListenerMethods();
83 if (listenerMethods.length > 0) {
84 Class[] args = getParameterTypes(getClass0(), listenerMethods[0]);
85 // Check for EventSet compliance. Special case for vetoableChange. See 4529996
86 if (!"vetoableChange".equals(eventSetName) && !args[0].getName().endsWith(eventName)) {
87 throw new IntrospectionException("Method \"" + listenerMethodName +
88 "\" should have argument \"" +
89 eventName + "\"");
90 }
91 }
92 }
93
94 private static String getListenerClassName(Class cls) {
95 String className = cls.getName();
96 return className.substring(className.lastIndexOf('.') + 1);
97 }
98
99 /**
100 * Creates an <TT>EventSetDescriptor</TT> from scratch using
101 * string names.
102 *
103 * @param sourceClass The class firing the event.
104 * @param eventSetName The programmatic name of the event set.
105 * Note that this should normally start with a lower-case character.
106 * @param listenerType The Class of the target interface that events
107 * will get delivered to.
108 * @param listenerMethodNames The names of the methods that will get called
109 * when the event gets delivered to its target listener interface.
110 * @param addListenerMethodName The name of the method on the event source
111 * that can be used to register an event listener object.
112 * @param removeListenerMethodName The name of the method on the event source
113 * that can be used to de-register an event listener object.
114 * @exception IntrospectionException if an exception occurs during
115 * introspection.
116 */
117 public EventSetDescriptor(Class<?> sourceClass,
118 String eventSetName,
119 Class<?> listenerType,
120 String listenerMethodNames[],
121 String addListenerMethodName,
122 String removeListenerMethodName)
123 throws IntrospectionException {
124 this(sourceClass, eventSetName, listenerType,
125 listenerMethodNames, addListenerMethodName,
126 removeListenerMethodName, null);
127 }
128
129 /**
130 * This constructor creates an EventSetDescriptor from scratch using
131 * string names.
132 *
133 * @param sourceClass The class firing the event.
134 * @param eventSetName The programmatic name of the event set.
135 * Note that this should normally start with a lower-case character.
136 * @param listenerType The Class of the target interface that events
137 * will get delivered to.
138 * @param listenerMethodNames The names of the methods that will get called
139 * when the event gets delivered to its target listener interface.
140 * @param addListenerMethodName The name of the method on the event source
141 * that can be used to register an event listener object.
142 * @param removeListenerMethodName The name of the method on the event source
143 * that can be used to de-register an event listener object.
144 * @param getListenerMethodName The method on the event source that
145 * can be used to access the array of event listener objects.
146 * @exception IntrospectionException if an exception occurs during
147 * introspection.
148 * @since 1.4
149 */
150 public EventSetDescriptor(Class<?> sourceClass,
151 String eventSetName,
152 Class<?> listenerType,
153 String listenerMethodNames[],
154 String addListenerMethodName,
155 String removeListenerMethodName,
156 String getListenerMethodName)
157 throws IntrospectionException {
158 if (sourceClass == null || eventSetName == null || listenerType == null) {
159 throw new NullPointerException();
160 }
161 setName(eventSetName);
162 setClass0(sourceClass);
163 setListenerType(listenerType);
164
165 Method[] listenerMethods = new Method[listenerMethodNames.length];
166 for (int i = 0; i < listenerMethodNames.length; i++) {
167 // Check for null names
168 if (listenerMethodNames[i] == null) {
169 throw new NullPointerException();
170 }
171 listenerMethods[i] = getMethod(listenerType, listenerMethodNames[i], 1);
172 }
173 setListenerMethods(listenerMethods);
174
175 setAddListenerMethod(getMethod(sourceClass, addListenerMethodName, 1));
176 setRemoveListenerMethod(getMethod(sourceClass, removeListenerMethodName, 1));
177
178 // Be more forgiving of not finding the getListener method.
179 Method method = Introspector.findMethod(sourceClass,
180 getListenerMethodName, 0);
181 if (method != null) {
182 setGetListenerMethod(method);
183 }
184 }
185
186 private static Method getMethod(Class cls, String name, int args)
187 throws IntrospectionException {
188 if (name == null) {
189 return null;
190 }
191 Method method = Introspector.findMethod(cls, name, args);
192 if (method == null) {
193 throw new IntrospectionException("Method not found: " + name +
194 " on class " + cls.getName());
195 }
196 return method;
197 }
198
199 /**
200 * Creates an <TT>EventSetDescriptor</TT> from scratch using
201 * <TT>java.lang.reflect.Method</TT> and <TT>java.lang.Class</TT> objects.
202 *
203 * @param eventSetName The programmatic name of the event set.
204 * @param listenerType The Class for the listener interface.
205 * @param listenerMethods An array of Method objects describing each
206 * of the event handling methods in the target listener.
207 * @param addListenerMethod The method on the event source
208 * that can be used to register an event listener object.
209 * @param removeListenerMethod The method on the event source
210 * that can be used to de-register an event listener object.
211 * @exception IntrospectionException if an exception occurs during
212 * introspection.
213 */
214 public EventSetDescriptor(String eventSetName,
215 Class<?> listenerType,
216 Method listenerMethods[],
217 Method addListenerMethod,
218 Method removeListenerMethod)
219 throws IntrospectionException {
220 this(eventSetName, listenerType, listenerMethods,
221 addListenerMethod, removeListenerMethod, null);
222 }
223
224 /**
225 * This constructor creates an EventSetDescriptor from scratch using
226 * java.lang.reflect.Method and java.lang.Class objects.
227 *
228 * @param eventSetName The programmatic name of the event set.
229 * @param listenerType The Class for the listener interface.
230 * @param listenerMethods An array of Method objects describing each
231 * of the event handling methods in the target listener.
232 * @param addListenerMethod The method on the event source
233 * that can be used to register an event listener object.
234 * @param removeListenerMethod The method on the event source
235 * that can be used to de-register an event listener object.
236 * @param getListenerMethod The method on the event source
237 * that can be used to access the array of event listener objects.
238 * @exception IntrospectionException if an exception occurs during
239 * introspection.
240 * @since 1.4
241 */
242 public EventSetDescriptor(String eventSetName,
243 Class<?> listenerType,
244 Method listenerMethods[],
245 Method addListenerMethod,
246 Method removeListenerMethod,
247 Method getListenerMethod)
248 throws IntrospectionException {
249 setName(eventSetName);
250 setListenerMethods(listenerMethods);
251 setAddListenerMethod(addListenerMethod);
252 setRemoveListenerMethod( removeListenerMethod);
253 setGetListenerMethod(getListenerMethod);
254 setListenerType(listenerType);
255 }
256
257 /**
258 * Creates an <TT>EventSetDescriptor</TT> from scratch using
259 * <TT>java.lang.reflect.MethodDescriptor</TT> and <TT>java.lang.Class</TT>
260 * objects.
261 *
262 * @param eventSetName The programmatic name of the event set.
263 * @param listenerType The Class for the listener interface.
264 * @param listenerMethodDescriptors An array of MethodDescriptor objects
265 * describing each of the event handling methods in the
266 * target listener.
267 * @param addListenerMethod The method on the event source
268 * that can be used to register an event listener object.
269 * @param removeListenerMethod The method on the event source
270 * that can be used to de-register an event listener object.
271 * @exception IntrospectionException if an exception occurs during
272 * introspection.
273 */
274 public EventSetDescriptor(String eventSetName,
275 Class<?> listenerType,
276 MethodDescriptor listenerMethodDescriptors[],
277 Method addListenerMethod,
278 Method removeListenerMethod)
279 throws IntrospectionException {
280 setName(eventSetName);
281 this.listenerMethodDescriptors = listenerMethodDescriptors;
282 setAddListenerMethod(addListenerMethod);
283 setRemoveListenerMethod(removeListenerMethod);
284 setListenerType(listenerType);
285 }
286
287 /**
288 * Gets the <TT>Class</TT> object for the target interface.
289 *
290 * @return The Class object for the target interface that will
291 * get invoked when the event is fired.
292 */
293 public Class<?> getListenerType() {
294 return (this.listenerTypeRef != null)
295 ? this.listenerTypeRef.get()
296 : null;
297 }
298
299 private void setListenerType(Class cls) {
300 this.listenerTypeRef = getWeakReference(cls);
301 }
302
303 /**
304 * Gets the methods of the target listener interface.
305 *
306 * @return An array of <TT>Method</TT> objects for the target methods
307 * within the target listener interface that will get called when
308 * events are fired.
309 */
310 public synchronized Method[] getListenerMethods() {
311 Method[] methods = getListenerMethods0();
312 if (methods == null) {
313 if (listenerMethodDescriptors != null) {
314 methods = new Method[listenerMethodDescriptors.length];
315 for (int i = 0; i < methods.length; i++) {
316 methods[i] = listenerMethodDescriptors[i].getMethod();
317 }
318 }
319 setListenerMethods(methods);
320 }
321 return methods;
322 }
323
324 private void setListenerMethods(Method[] methods) {
325 if (methods == null) {
326 return;
327 }
328 if (listenerMethodDescriptors == null) {
329 listenerMethodDescriptors = new MethodDescriptor[methods.length];
330 for (int i = 0; i < methods.length; i++) {
331 listenerMethodDescriptors[i] = new MethodDescriptor(methods[i]);
332 }
333 }
334 this.listenerMethodsRef = getSoftReference(methods);
335 }
336
337 private Method[] getListenerMethods0() {
338 return (this.listenerMethodsRef != null)
339 ? this.listenerMethodsRef.get()
340 : null;
341 }
342
343 /**
344 * Gets the <code>MethodDescriptor</code>s of the target listener interface.
345 *
346 * @return An array of <code>MethodDescriptor</code> objects for the target methods
347 * within the target listener interface that will get called when
348 * events are fired.
349 */
350 public synchronized MethodDescriptor[] getListenerMethodDescriptors() {
351 return listenerMethodDescriptors;
352 }
353
354 /**
355 * Gets the method used to add event listeners.
356 *
357 * @return The method used to register a listener at the event source.
358 */
359 public synchronized Method getAddListenerMethod() {
360 return (addMethodDescriptor != null ?
361 addMethodDescriptor.getMethod() : null);
362 }
363
364 private synchronized void setAddListenerMethod(Method method) {
365 if (method == null) {
366 return;
367 }
368 if (getClass0() == null) {
369 setClass0(method.getDeclaringClass());
370 }
371 addMethodDescriptor = new MethodDescriptor(method);
372 setTransient(method.getAnnotation(Transient.class));
373 }
374
375 /**
376 * Gets the method used to remove event listeners.
377 *
378 * @return The method used to remove a listener at the event source.
379 */
380 public synchronized Method getRemoveListenerMethod() {
381 return (removeMethodDescriptor != null ?
382 removeMethodDescriptor.getMethod() : null);
383 }
384
385 private synchronized void setRemoveListenerMethod(Method method) {
386 if (method == null) {
387 return;
388 }
389 if (getClass0() == null) {
390 setClass0(method.getDeclaringClass());
391 }
392 removeMethodDescriptor = new MethodDescriptor(method);
393 setTransient(method.getAnnotation(Transient.class));
394 }
395
396 /**
397 * Gets the method used to access the registered event listeners.
398 *
399 * @return The method used to access the array of listeners at the event
400 * source or null if it doesn't exist.
401 * @since 1.4
402 */
403 public synchronized Method getGetListenerMethod() {
404 return (getMethodDescriptor != null ?
405 getMethodDescriptor.getMethod() : null);
406 }
407
408 private synchronized void setGetListenerMethod(Method method) {
409 if (method == null) {
410 return;
411 }
412 if (getClass0() == null) {
413 setClass0(method.getDeclaringClass());
414 }
415 getMethodDescriptor = new MethodDescriptor(method);
416 setTransient(method.getAnnotation(Transient.class));
417 }
418
419 /**
420 * Mark an event set as unicast (or not).
421 *
422 * @param unicast True if the event set is unicast.
423 */
424 public void setUnicast(boolean unicast) {
425 this.unicast = unicast;
426 }
427
428 /**
429 * Normally event sources are multicast. However there are some
430 * exceptions that are strictly unicast.
431 *
432 * @return <TT>true</TT> if the event set is unicast.
433 * Defaults to <TT>false</TT>.
434 */
435 public boolean isUnicast() {
436 return unicast;
437 }
438
439 /**
440 * Marks an event set as being in the "default" set (or not).
441 * By default this is <TT>true</TT>.
442 *
443 * @param inDefaultEventSet <code>true</code> if the event set is in
444 * the "default" set,
445 * <code>false</code> if not
446 */
447 public void setInDefaultEventSet(boolean inDefaultEventSet) {
448 this.inDefaultEventSet = inDefaultEventSet;
449 }
450
451 /**
452 * Reports if an event set is in the "default" set.
453 *
454 * @return <TT>true</TT> if the event set is in
455 * the "default" set. Defaults to <TT>true</TT>.
456 */
457 public boolean isInDefaultEventSet() {
458 return inDefaultEventSet;
459 }
460
461 /*
462 * Package-private constructor
463 * Merge two event set descriptors. Where they conflict, give the
464 * second argument (y) priority over the first argument (x).
465 *
466 * @param x The first (lower priority) EventSetDescriptor
467 * @param y The second (higher priority) EventSetDescriptor
468 */
469 EventSetDescriptor(EventSetDescriptor x, EventSetDescriptor y) {
470 super(x,y);
471 listenerMethodDescriptors = x.listenerMethodDescriptors;
472 if (y.listenerMethodDescriptors != null) {
473 listenerMethodDescriptors = y.listenerMethodDescriptors;
474 }
475
476 listenerTypeRef = x.listenerTypeRef;
477 if (y.listenerTypeRef != null) {
478 listenerTypeRef = y.listenerTypeRef;
479 }
480
481 addMethodDescriptor = x.addMethodDescriptor;
482 if (y.addMethodDescriptor != null) {
483 addMethodDescriptor = y.addMethodDescriptor;
484 }
485
486 removeMethodDescriptor = x.removeMethodDescriptor;
487 if (y.removeMethodDescriptor != null) {
488 removeMethodDescriptor = y.removeMethodDescriptor;
489 }
490
491 getMethodDescriptor = x.getMethodDescriptor;
492 if (y.getMethodDescriptor != null) {
493 getMethodDescriptor = y.getMethodDescriptor;
494 }
495
496 unicast = y.unicast;
497 if (!x.inDefaultEventSet || !y.inDefaultEventSet) {
498 inDefaultEventSet = false;
499 }
500 }
501
502 /*
503 * Package-private dup constructor
504 * This must isolate the new object from any changes to the old object.
505 */
506 EventSetDescriptor(EventSetDescriptor old) {
507 super(old);
508 if (old.listenerMethodDescriptors != null) {
509 int len = old.listenerMethodDescriptors.length;
510 listenerMethodDescriptors = new MethodDescriptor[len];
511 for (int i = 0; i < len; i++) {
512 listenerMethodDescriptors[i] = new MethodDescriptor(
513 old.listenerMethodDescriptors[i]);
514 }
515 }
516 listenerTypeRef = old.listenerTypeRef;
517
518 addMethodDescriptor = old.addMethodDescriptor;
519 removeMethodDescriptor = old.removeMethodDescriptor;
520 getMethodDescriptor = old.getMethodDescriptor;
521
522 unicast = old.unicast;
523 inDefaultEventSet = old.inDefaultEventSet;
524 }
525 }