1 /* 2 * Copyright (c) 1996, 2012, 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 26 package java.beans; 27 28 import java.lang.ref.Reference; 29 import java.lang.reflect.Method; 30 import java.lang.reflect.Modifier; 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<? extends 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, getListenerMethodName, 0); 180 if (method != null) { 181 setGetListenerMethod(method); 182 } 183 } 184 185 private static Method getMethod(Class<?> cls, String name, int args) 186 throws IntrospectionException { 187 if (name == null) { 188 return null; 189 } 190 Method method = Introspector.findMethod(cls, name, args); 191 if ((method == null) || Modifier.isStatic(method.getModifiers())) { 192 throw new IntrospectionException("Method not found: " + name + 193 " on class " + cls.getName()); 194 } 195 return method; 196 } 197 198 /** 199 * Creates an <TT>EventSetDescriptor</TT> from scratch using 200 * <TT>java.lang.reflect.Method</TT> and <TT>java.lang.Class</TT> objects. 201 * 202 * @param eventSetName The programmatic name of the event set. 203 * @param listenerType The Class for the listener interface. 204 * @param listenerMethods An array of Method objects describing each 205 * of the event handling methods in the target listener. 206 * @param addListenerMethod The method on the event source 207 * that can be used to register an event listener object. 208 * @param removeListenerMethod The method on the event source 209 * that can be used to de-register an event listener object. 210 * @exception IntrospectionException if an exception occurs during 211 * introspection. 212 */ 213 public EventSetDescriptor(String eventSetName, 214 Class<?> listenerType, 215 Method listenerMethods[], 216 Method addListenerMethod, 217 Method removeListenerMethod) 218 throws IntrospectionException { 219 this(eventSetName, listenerType, listenerMethods, 220 addListenerMethod, removeListenerMethod, null); 221 } 222 223 /** 224 * This constructor creates an EventSetDescriptor from scratch using 225 * java.lang.reflect.Method and java.lang.Class objects. 226 * 227 * @param eventSetName The programmatic name of the event set. 228 * @param listenerType The Class for the listener interface. 229 * @param listenerMethods An array of Method objects describing each 230 * of the event handling methods in the target listener. 231 * @param addListenerMethod The method on the event source 232 * that can be used to register an event listener object. 233 * @param removeListenerMethod The method on the event source 234 * that can be used to de-register an event listener object. 235 * @param getListenerMethod The method on the event source 236 * that can be used to access the array of event listener objects. 237 * @exception IntrospectionException if an exception occurs during 238 * introspection. 239 * @since 1.4 240 */ 241 public EventSetDescriptor(String eventSetName, 242 Class<?> listenerType, 243 Method listenerMethods[], 244 Method addListenerMethod, 245 Method removeListenerMethod, 246 Method getListenerMethod) 247 throws IntrospectionException { 248 setName(eventSetName); 249 setListenerMethods(listenerMethods); 250 setAddListenerMethod(addListenerMethod); 251 setRemoveListenerMethod( removeListenerMethod); 252 setGetListenerMethod(getListenerMethod); 253 setListenerType(listenerType); 254 } 255 256 /** 257 * Creates an <TT>EventSetDescriptor</TT> from scratch using 258 * <TT>java.lang.reflect.MethodDescriptor</TT> and <TT>java.lang.Class</TT> 259 * objects. 260 * 261 * @param eventSetName The programmatic name of the event set. 262 * @param listenerType The Class for the listener interface. 263 * @param listenerMethodDescriptors An array of MethodDescriptor objects 264 * describing each of the event handling methods in the 265 * target listener. 266 * @param addListenerMethod The method on the event source 267 * that can be used to register an event listener object. 268 * @param removeListenerMethod The method on the event source 269 * that can be used to de-register an event listener object. 270 * @exception IntrospectionException if an exception occurs during 271 * introspection. 272 */ 273 public EventSetDescriptor(String eventSetName, 274 Class<?> listenerType, 275 MethodDescriptor listenerMethodDescriptors[], 276 Method addListenerMethod, 277 Method removeListenerMethod) 278 throws IntrospectionException { 279 setName(eventSetName); 280 this.listenerMethodDescriptors = (listenerMethodDescriptors != null) 281 ? listenerMethodDescriptors.clone() 282 : null; 283 setAddListenerMethod(addListenerMethod); 284 setRemoveListenerMethod(removeListenerMethod); 285 setListenerType(listenerType); 286 } 287 288 /** 289 * Gets the <TT>Class</TT> object for the target interface. 290 * 291 * @return The Class object for the target interface that will 292 * get invoked when the event is fired. 293 */ 294 public Class<?> getListenerType() { 295 return (this.listenerTypeRef != null) 296 ? this.listenerTypeRef.get() 297 : null; 298 } 299 300 private void setListenerType(Class<?> cls) { 301 this.listenerTypeRef = getWeakReference(cls); 302 } 303 304 /** 305 * Gets the methods of the target listener interface. 306 * 307 * @return An array of <TT>Method</TT> objects for the target methods 308 * within the target listener interface that will get called when 309 * events are fired. 310 */ 311 public synchronized Method[] getListenerMethods() { 312 Method[] methods = getListenerMethods0(); 313 if (methods == null) { 314 if (listenerMethodDescriptors != null) { 315 methods = new Method[listenerMethodDescriptors.length]; 316 for (int i = 0; i < methods.length; i++) { 317 methods[i] = listenerMethodDescriptors[i].getMethod(); 318 } 319 } 320 setListenerMethods(methods); 321 } 322 return methods; 323 } 324 325 private void setListenerMethods(Method[] methods) { 326 if (methods == null) { 327 return; 328 } 329 if (listenerMethodDescriptors == null) { 330 listenerMethodDescriptors = new MethodDescriptor[methods.length]; 331 for (int i = 0; i < methods.length; i++) { 332 listenerMethodDescriptors[i] = new MethodDescriptor(methods[i]); 333 } 334 } 335 this.listenerMethodsRef = getSoftReference(methods); 336 } 337 338 private Method[] getListenerMethods0() { 339 return (this.listenerMethodsRef != null) 340 ? this.listenerMethodsRef.get() 341 : null; 342 } 343 344 /** 345 * Gets the <code>MethodDescriptor</code>s of the target listener interface. 346 * 347 * @return An array of <code>MethodDescriptor</code> objects for the target methods 348 * within the target listener interface that will get called when 349 * events are fired. 350 */ 351 public synchronized MethodDescriptor[] getListenerMethodDescriptors() { 352 return (this.listenerMethodDescriptors != null) 353 ? this.listenerMethodDescriptors.clone() 354 : null; 355 } 356 357 /** 358 * Gets the method used to add event listeners. 359 * 360 * @return The method used to register a listener at the event source. 361 */ 362 public synchronized Method getAddListenerMethod() { 363 return getMethod(this.addMethodDescriptor); 364 } 365 366 private synchronized void setAddListenerMethod(Method method) { 367 if (method == null) { 368 return; 369 } 370 if (getClass0() == null) { 371 setClass0(method.getDeclaringClass()); 372 } 373 addMethodDescriptor = new MethodDescriptor(method); 374 setTransient(method.getAnnotation(Transient.class)); 375 } 376 377 /** 378 * Gets the method used to remove event listeners. 379 * 380 * @return The method used to remove a listener at the event source. 381 */ 382 public synchronized Method getRemoveListenerMethod() { 383 return getMethod(this.removeMethodDescriptor); 384 } 385 386 private synchronized void setRemoveListenerMethod(Method method) { 387 if (method == null) { 388 return; 389 } 390 if (getClass0() == null) { 391 setClass0(method.getDeclaringClass()); 392 } 393 removeMethodDescriptor = new MethodDescriptor(method); 394 setTransient(method.getAnnotation(Transient.class)); 395 } 396 397 /** 398 * Gets the method used to access the registered event listeners. 399 * 400 * @return The method used to access the array of listeners at the event 401 * source or null if it doesn't exist. 402 * @since 1.4 403 */ 404 public synchronized Method getGetListenerMethod() { 405 return getMethod(this.getMethodDescriptor); 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 526 void appendTo(StringBuilder sb) { 527 appendTo(sb, "unicast", this.unicast); 528 appendTo(sb, "inDefaultEventSet", this.inDefaultEventSet); 529 appendTo(sb, "listenerType", this.listenerTypeRef); 530 appendTo(sb, "getListenerMethod", getMethod(this.getMethodDescriptor)); 531 appendTo(sb, "addListenerMethod", getMethod(this.addMethodDescriptor)); 532 appendTo(sb, "removeListenerMethod", getMethod(this.removeMethodDescriptor)); 533 } 534 535 private static Method getMethod(MethodDescriptor descriptor) { 536 return (descriptor != null) 537 ? descriptor.getMethod() 538 : null; 539 } 540 }