1 /* 2 * Copyright (c) 1996, 2018, 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 */ EventSetDescriptor(Class<?> sourceClass, String eventSetName, Class<?> listenerType, String listenerMethodName)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 getListenerClassName(Class<?> cls)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 */ EventSetDescriptor(Class<?> sourceClass, String eventSetName, Class<?> listenerType, String[] listenerMethodNames, String addListenerMethodName, String removeListenerMethodName)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 */ EventSetDescriptor(Class<?> sourceClass, String eventSetName, Class<?> listenerType, String[] listenerMethodNames, String addListenerMethodName, String removeListenerMethodName, String getListenerMethodName)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 getMethod(Class<?> cls, String name, int args)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 */ EventSetDescriptor(String eventSetName, Class<?> listenerType, Method[] listenerMethods, Method addListenerMethod, Method removeListenerMethod)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 */ EventSetDescriptor(String eventSetName, Class<?> listenerType, Method[] listenerMethods, Method addListenerMethod, Method removeListenerMethod, Method getListenerMethod)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 EventSetDescriptor(String base, EventSetInfo info, Method... methods)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 */ EventSetDescriptor(String eventSetName, Class<?> listenerType, MethodDescriptor[] listenerMethodDescriptors, Method addListenerMethod, Method removeListenerMethod)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 */ getListenerType()307 public Class<?> getListenerType() { 308 return (this.listenerTypeRef != null) 309 ? this.listenerTypeRef.get() 310 : null; 311 } 312 setListenerType(Class<?> cls)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 */ getListenerMethods()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 setListenerMethods(Method[] methods)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 getListenerMethods0()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 */ getListenerMethodDescriptors()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 */ getAddListenerMethod()375 public synchronized Method getAddListenerMethod() { 376 return getMethod(this.addMethodDescriptor); 377 } 378 setAddListenerMethod(Method method)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 */ getRemoveListenerMethod()395 public synchronized Method getRemoveListenerMethod() { 396 return getMethod(this.removeMethodDescriptor); 397 } 398 setRemoveListenerMethod(Method method)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 */ getGetListenerMethod()417 public synchronized Method getGetListenerMethod() { 418 return getMethod(this.getMethodDescriptor); 419 } 420 setGetListenerMethod(Method method)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 */ setUnicast(boolean unicast)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 */ isUnicast()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 */ setInDefaultEventSet(boolean inDefaultEventSet)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 */ isInDefaultEventSet()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 */ EventSetDescriptor(EventSetDescriptor x, EventSetDescriptor y)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 */ EventSetDescriptor(EventSetDescriptor old)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 appendTo(StringBuilder sb)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 getMethod(MethodDescriptor descriptor)548 private static Method getMethod(MethodDescriptor descriptor) { 549 return (descriptor != null) 550 ? descriptor.getMethod() 551 : null; 552 } 553 } 554