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 <TT>EventSetDescriptor</TT> 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 <TT>EventSetDescriptor</TT> 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[], 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 <TT>EventSetDescriptor</TT> from scratch using 203 * <TT>java.lang.reflect.Method</TT> and <TT>java.lang.Class</TT> 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); 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 <TT>EventSetDescriptor</TT> from scratch using 271 * <TT>java.lang.reflect.MethodDescriptor</TT> and <TT>java.lang.Class</TT> 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 <TT>Class</TT> 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 <TT>Method</TT> 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</code>s of the target listener interface. 359 * 360 * @return An array of <code>MethodDescriptor</code> 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) { 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 <TT>true</TT> if the event set is unicast. 446 * Defaults to <TT>false</TT>. 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 <TT>true</TT>. 455 * 456 * @param inDefaultEventSet <code>true</code> if the event set is in 457 * the "default" set, 458 * <code>false</code> 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 <TT>true</TT> if the event set is in 468 * the "default" set. Defaults to <TT>true</TT>. 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 | 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[], 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); 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) { 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 |