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