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 }