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